summaryrefslogtreecommitdiff
path: root/lib/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/Makefile4
-rw-r--r--lib/stdlib/doc/src/Makefile74
-rw-r--r--lib/stdlib/doc/src/array.xml172
-rw-r--r--lib/stdlib/doc/src/assert_hrl.xml4
-rw-r--r--lib/stdlib/doc/src/beam_lib.xml26
-rw-r--r--lib/stdlib/doc/src/binary.xml46
-rw-r--r--lib/stdlib/doc/src/c.xml88
-rw-r--r--lib/stdlib/doc/src/calendar.xml14
-rw-r--r--lib/stdlib/doc/src/dets.xml190
-rw-r--r--lib/stdlib/doc/src/dict.xml16
-rw-r--r--lib/stdlib/doc/src/digraph.xml78
-rw-r--r--lib/stdlib/doc/src/digraph_utils.xml80
-rw-r--r--lib/stdlib/doc/src/epp.xml24
-rw-r--r--lib/stdlib/doc/src/erl_anno.xml38
-rw-r--r--lib/stdlib/doc/src/erl_eval.xml44
-rw-r--r--lib/stdlib/doc/src/erl_expand_records.xml2
-rw-r--r--lib/stdlib/doc/src/erl_id_trans.xml10
-rw-r--r--lib/stdlib/doc/src/erl_lint.xml20
-rw-r--r--lib/stdlib/doc/src/erl_parse.xml71
-rw-r--r--lib/stdlib/doc/src/erl_pp.xml26
-rw-r--r--lib/stdlib/doc/src/erl_scan.xml18
-rw-r--r--lib/stdlib/doc/src/erl_tar.xml113
-rw-r--r--lib/stdlib/doc/src/ets.xml541
-rw-r--r--lib/stdlib/doc/src/file_sorter.xml2
-rw-r--r--lib/stdlib/doc/src/filelib.xml52
-rw-r--r--lib/stdlib/doc/src/filename.xml44
-rw-r--r--lib/stdlib/doc/src/gb_sets.xml52
-rw-r--r--lib/stdlib/doc/src/gb_trees.xml12
-rw-r--r--lib/stdlib/doc/src/gen_event.xml310
-rw-r--r--lib/stdlib/doc/src/gen_fsm.xml4
-rw-r--r--lib/stdlib/doc/src/gen_server.xml323
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml933
-rw-r--r--lib/stdlib/doc/src/io.xml74
-rw-r--r--lib/stdlib/doc/src/io_lib.xml30
-rw-r--r--lib/stdlib/doc/src/io_protocol.xml65
-rw-r--r--lib/stdlib/doc/src/lists.xml32
-rw-r--r--lib/stdlib/doc/src/log_mf_h.xml10
-rw-r--r--lib/stdlib/doc/src/maps.xml12
-rw-r--r--lib/stdlib/doc/src/math.xml4
-rw-r--r--lib/stdlib/doc/src/ms_transform.xml48
-rw-r--r--lib/stdlib/doc/src/notes.xml529
-rw-r--r--lib/stdlib/doc/src/orddict.xml16
-rw-r--r--lib/stdlib/doc/src/ordsets.xml8
-rw-r--r--lib/stdlib/doc/src/part.xml1
-rw-r--r--lib/stdlib/doc/src/pool.xml10
-rw-r--r--lib/stdlib/doc/src/proc_lib.xml166
-rw-r--r--lib/stdlib/doc/src/proplists.xml64
-rw-r--r--lib/stdlib/doc/src/qlc.xml218
-rw-r--r--lib/stdlib/doc/src/queue.xml33
-rw-r--r--lib/stdlib/doc/src/rand.xml38
-rw-r--r--lib/stdlib/doc/src/random.xml30
-rw-r--r--lib/stdlib/doc/src/re.xml172
-rw-r--r--lib/stdlib/doc/src/ref_man.xml1
-rw-r--r--lib/stdlib/doc/src/sets.xml8
-rw-r--r--lib/stdlib/doc/src/shell.xml35
-rw-r--r--lib/stdlib/doc/src/shell_default.xml2
-rw-r--r--lib/stdlib/doc/src/shell_docs.xml166
-rw-r--r--lib/stdlib/doc/src/slave.xml6
-rw-r--r--lib/stdlib/doc/src/sofs.xml324
-rw-r--r--lib/stdlib/doc/src/specs.xml1
-rw-r--r--lib/stdlib/doc/src/stdlib_app.xml8
-rw-r--r--lib/stdlib/doc/src/string.xml173
-rw-r--r--lib/stdlib/doc/src/supervisor.xml109
-rw-r--r--lib/stdlib/doc/src/supervisor_bridge.xml29
-rw-r--r--lib/stdlib/doc/src/sys.xml127
-rw-r--r--lib/stdlib/doc/src/timer.xml49
-rw-r--r--lib/stdlib/doc/src/unicode.xml40
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml164
-rw-r--r--lib/stdlib/doc/src/uri_string.xml91
-rw-r--r--lib/stdlib/doc/src/uri_string_usage.xml370
-rw-r--r--lib/stdlib/doc/src/win32reg.xml14
-rw-r--r--lib/stdlib/doc/src/zip.xml68
-rw-r--r--lib/stdlib/examples/erl_id_trans.erl8
-rw-r--r--lib/stdlib/include/erl_bits.hrl16
-rwxr-xr-xlib/stdlib/scripts/update_deprecations359
-rw-r--r--lib/stdlib/src/Makefile11
-rw-r--r--lib/stdlib/src/beam_lib.erl13
-rw-r--r--lib/stdlib/src/c.erl212
-rw-r--r--lib/stdlib/src/calendar.erl3
-rw-r--r--lib/stdlib/src/digraph.erl4
-rw-r--r--lib/stdlib/src/edlin.erl5
-rw-r--r--lib/stdlib/src/edlin_expand.erl78
-rw-r--r--lib/stdlib/src/epp.erl2
-rw-r--r--lib/stdlib/src/erl_error.erl219
-rw-r--r--lib/stdlib/src/erl_eval.erl26
-rw-r--r--lib/stdlib/src/erl_expand_records.erl18
-rw-r--r--lib/stdlib/src/erl_internal.erl16
-rw-r--r--lib/stdlib/src/erl_lint.erl481
-rw-r--r--lib/stdlib/src/erl_parse.yrl33
-rw-r--r--lib/stdlib/src/erl_pp.erl84
-rw-r--r--lib/stdlib/src/erl_scan.erl160
-rw-r--r--lib/stdlib/src/erl_tar.erl85
-rw-r--r--lib/stdlib/src/escript.erl38
-rw-r--r--lib/stdlib/src/ets.erl4
-rw-r--r--lib/stdlib/src/eval_bits.erl46
-rw-r--r--lib/stdlib/src/filelib.erl145
-rw-r--r--lib/stdlib/src/filename.erl4
-rw-r--r--lib/stdlib/src/gen.erl97
-rw-r--r--lib/stdlib/src/gen_event.erl256
-rw-r--r--lib/stdlib/src/gen_fsm.erl343
-rw-r--r--lib/stdlib/src/gen_server.erl348
-rw-r--r--lib/stdlib/src/gen_statem.erl450
-rw-r--r--lib/stdlib/src/io.erl12
-rw-r--r--lib/stdlib/src/io_lib.erl29
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl3
-rw-r--r--lib/stdlib/src/lists.erl8
-rw-r--r--lib/stdlib/src/maps.erl3
-rw-r--r--lib/stdlib/src/otp_internal.erl1070
-rw-r--r--lib/stdlib/src/otp_internal.hrl36
-rw-r--r--lib/stdlib/src/proc_lib.erl411
-rw-r--r--lib/stdlib/src/proplists.erl20
-rw-r--r--lib/stdlib/src/qlc.erl4
-rw-r--r--lib/stdlib/src/qlc_pt.erl28
-rw-r--r--lib/stdlib/src/queue.erl2
-rw-r--r--lib/stdlib/src/random.erl2
-rw-r--r--lib/stdlib/src/shell.erl61
-rw-r--r--lib/stdlib/src/shell_default.erl21
-rw-r--r--lib/stdlib/src/shell_docs.erl1018
-rw-r--r--lib/stdlib/src/stdlib.app.src3
-rw-r--r--lib/stdlib/src/stdlib.appup.src15
-rw-r--r--lib/stdlib/src/supervisor.erl269
-rw-r--r--lib/stdlib/src/supervisor_bridge.erl176
-rw-r--r--lib/stdlib/src/sys.erl5
-rw-r--r--lib/stdlib/src/uri_string.erl155
-rw-r--r--lib/stdlib/src/zip.erl148
-rw-r--r--lib/stdlib/test/Makefile6
-rw-r--r--lib/stdlib/test/beam_lib_SUITE.erl28
-rw-r--r--lib/stdlib/test/dict_test_lib.erl5
-rw-r--r--lib/stdlib/test/digraph_SUITE.erl51
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE.erl32
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps.erl (renamed from lib/stdlib/test/ExpandTestCaps.erl)0
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps1.erl (renamed from lib/stdlib/test/ExpandTestCaps1.erl)0
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/expand_test.erl (renamed from lib/stdlib/test/expand_test.erl)0
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/expand_test1.erl (renamed from lib/stdlib/test/expand_test1.erl)0
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE_data/unicode_expand.erl (renamed from lib/stdlib/test/unicode_expand.erl)0
-rw-r--r--lib/stdlib/test/epp_SUITE.erl66
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl73
-rw-r--r--lib/stdlib/test/erl_expand_records_SUITE.erl6
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl226
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl36
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl111
-rw-r--r--lib/stdlib/test/ets_SUITE.erl800
-rw-r--r--lib/stdlib/test/ets_SUITE_data/visualize_throughput.html135
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl172
-rw-r--r--lib/stdlib/test/filename_SUITE.erl7
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl302
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl239
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl344
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl619
-rw-r--r--lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl18
-rw-r--r--lib/stdlib/test/io_proto_SUITE.erl20
-rw-r--r--lib/stdlib/test/lists_SUITE.erl2
-rw-r--r--lib/stdlib/test/maps_SUITE.erl33
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl372
-rw-r--r--lib/stdlib/test/property_test/shell_docs_prop.erl135
-rw-r--r--lib/stdlib/test/property_test/uri_string_recompose.erl9
-rw-r--r--lib/stdlib/test/re_SUITE_data/testoutput23
-rw-r--r--lib/stdlib/test/shell_SUITE.erl51
-rw-r--r--lib/stdlib/test/shell_docs_SUITE.erl237
-rw-r--r--lib/stdlib/test/stdlib_SUITE.erl4
-rw-r--r--lib/stdlib/test/string_SUITE.erl20
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl323
-rw-r--r--lib/stdlib/test/supervisor_bridge_SUITE.erl206
-rw-r--r--lib/stdlib/test/tar_SUITE.erl28
-rw-r--r--lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt78
-rw-r--r--lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt6
-rw-r--r--lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt34
-rw-r--r--lib/stdlib/test/uri_string_SUITE.erl182
-rw-r--r--lib/stdlib/test/win32reg_SUITE.erl38
-rw-r--r--lib/stdlib/test/zip_SUITE.erl203
-rw-r--r--lib/stdlib/test/zip_SUITE_data/zip-latin1.zipbin0 -> 115 bytes
-rw-r--r--lib/stdlib/uc_spec/CaseFolding.txt13
-rw-r--r--lib/stdlib/uc_spec/CompositionExclusions.txt6
-rw-r--r--lib/stdlib/uc_spec/GraphemeBreakProperty.txt39
-rw-r--r--lib/stdlib/uc_spec/PropList.txt77
-rw-r--r--lib/stdlib/uc_spec/README-UPDATE.txt10
-rw-r--r--lib/stdlib/uc_spec/SpecialCasing.txt6
-rw-r--r--lib/stdlib/uc_spec/UnicodeData.txt565
-rw-r--r--lib/stdlib/uc_spec/emoji-data.txt305
-rw-r--r--lib/stdlib/uc_spec/gen_unicode_mod.escript2
-rw-r--r--lib/stdlib/vsn.mk2
181 files changed, 14973 insertions, 5151 deletions
diff --git a/lib/stdlib/Makefile b/lib/stdlib/Makefile
index 3086d85445..cae3844126 100644
--- a/lib/stdlib/Makefile
+++ b/lib/stdlib/Makefile
@@ -35,3 +35,7 @@ SPECIAL_TARGETS =
# Default Subdir Targets
#
include $(ERL_TOP)/make/otp_subdir.mk
+
+DIA_PLT_APPS=compiler crypto
+
+include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index 4541b4a463..4b22e35e3b 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -28,11 +28,6 @@ VSN=$(STDLIB_VSN)
APPLICATION=stdlib
# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
-
-# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = ref_man.xml
@@ -89,6 +84,7 @@ XML_REF3_FILES = \
sets.xml \
shell.xml \
shell_default.xml \
+ shell_docs.xml \
slave.xml \
sofs.xml \
string.xml \
@@ -105,7 +101,7 @@ XML_REF6_FILES = stdlib_app.xml
XML_PART_FILES = part.xml
XML_CHAPTER_FILES = introduction.xml io_protocol.xml unicode_usage.xml \
- notes.xml assert_hrl.xml
+ uri_string_usage.xml notes.xml assert_hrl.xml
BOOK_FILES = book.xml
@@ -113,74 +109,12 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) $(XML_APPLICATION_FILES)
-# ----------------------------------------------------
-
-HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
- $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
-
-INFO_FILE = ../../info
-
-MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
-MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
-
-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
-# ----------------------------------------------------
-docs: man pdf html
-
-$(TOP_PDF_FILE): $(XML_FILES)
-
-pdf: $(TOP_PDF_FILE)
-
-html: $(HTML_REF_MAN_FILE)
-
-man: $(MAN3_FILES) $(MAN6_FILES)
-
-debug opt:
-
-clean clean_docs:
- rm -rf $(HTMLDIR)/*
- rm -rf $(XMLDIR)
- 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:
$(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module erl_id_trans
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_docs_spec: docs
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
- $(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(HTMLDIR)/* \
- "$(RELSYSDIR)/doc/html"
- $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3"
- $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6"
- $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6"
+NO_CHUNKS = erl_id_trans.xml
-release_spec:
+include $(ERL_TOP)/make/doc.mk
diff --git a/lib/stdlib/doc/src/array.xml b/lib/stdlib/doc/src/array.xml
index 3607c4f162..bb18489994 100644
--- a/lib/stdlib/doc/src/array.xml
+++ b/lib/stdlib/doc/src/array.xml
@@ -47,14 +47,14 @@
value is the atom <c>undefined</c>. There is no difference between an
unset entry and an entry that has been explicitly set to the same value
as the default one (compare
- <seealso marker="#reset-2"><c>reset/2</c></seealso>). If you need to
+ <seemfa marker="#reset/2"><c>reset/2</c></seemfa>). If you need to
differentiate between unset and set entries, ensure that the default value
cannot be confused with the values of set entries.</p>
<p>The array never shrinks automatically. If an index <c>I</c> has been used
to set an entry successfully, all indices in the range [0,<c>I</c>] stay
accessible unless the array size is explicitly changed by calling
- <seealso marker="#resize-2"><c>resize/2</c></seealso>.</p>
+ <seemfa marker="#resize/2"><c>resize/2</c></seemfa>.</p>
<p><em>Examples:</em></p>
@@ -139,20 +139,20 @@ A3 = array:fix(A2).</pre>
<func>
<name name="default" arity="1" since=""/>
<fsummary>Get the value used for uninitialized entries.</fsummary>
- <desc><marker id="default-1"/>
+ <desc>
<p>Gets the value used for uninitialized entries.</p>
- <p>See also <seealso marker="#new-2"><c>new/2</c></seealso>.</p>
+ <p>See also <seemfa marker="#new/2"><c>new/2</c></seemfa>.</p>
</desc>
</func>
<func>
<name name="fix" arity="1" since=""/>
<fsummary>Fix the array size.</fsummary>
- <desc><marker id="fix-1"/>
+ <desc>
<p>Fixes the array size. This prevents it from growing automatically
upon insertion.</p>
- <p>See also <seealso marker="#set-3"><c>set/3</c></seealso> and
- <seealso marker="#relax-1"><c>relax/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#set/3"><c>set/3</c></seemfa> and
+ <seemfa marker="#relax/1"><c>relax/1</c></seemfa>.</p>
</desc>
</func>
@@ -160,14 +160,14 @@ A3 = array:fix(A2).</pre>
<name name="foldl" arity="3" since=""/>
<fsummary>Fold the array elements using the specified function and initial
accumulator value.</fsummary>
- <desc><marker id="foldl-3"/>
+ <desc>
<p>Folds the array elements using the specified function and initial
accumulator value. The elements are visited in order from 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>See also <seealso marker="#foldr-3"><c>foldr/3</c></seealso>,
- <seealso marker="#map-2"><c>map/2</c></seealso>,
- <seealso marker="#sparse_foldl-3"><c>sparse_foldl/3</c></seealso>.</p>
+ <p>See also <seemfa marker="#foldr/3"><c>foldr/3</c></seemfa>,
+ <seemfa marker="#map/2"><c>map/2</c></seemfa>,
+ <seemfa marker="#sparse_foldl/3"><c>sparse_foldl/3</c></seemfa>.</p>
</desc>
</func>
@@ -175,35 +175,35 @@ A3 = array:fix(A2).</pre>
<name name="foldr" arity="3" since=""/>
<fsummary>Fold the array elements right-to-left using the specified
function and initial accumulator value.</fsummary>
- <desc><marker id="foldr-3"/>
+ <desc>
<p>Folds the array elements right-to-left using the specified function
and initial accumulator value. The elements are visited in 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>See also <seealso marker="#foldl-3"><c>foldl/3</c></seealso>,
- <seealso marker="#map-2"><c>map/2</c></seealso>.</p>
+ <p>See also <seemfa marker="#foldl/3"><c>foldl/3</c></seemfa>,
+ <seemfa marker="#map/2"><c>map/2</c></seemfa>.</p>
</desc>
</func>
<func>
<name name="from_list" arity="1" since=""/>
<fsummary>Equivalent to <c>from_list(List, undefined)</c>.</fsummary>
- <desc><marker id="from_list-1"/>
+ <desc>
<p>Equivalent to
- <seealso marker="#from_list-2"><c>from_list(<anno>List</anno>, undefined)</c></seealso>.</p>
+ <seemfa marker="#from_list/2"><c>from_list(<anno>List</anno>, undefined)</c></seemfa>.</p>
</desc>
</func>
<func>
<name name="from_list" arity="2" since=""/>
<fsummary>Convert a list to an extendible array.</fsummary>
- <desc><marker id="from_list-2"/>
+ <desc>
<p>Converts 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>See also <seealso marker="#new-2"><c>new/2</c></seealso>,
- <seealso marker="#to_list-1"><c>to_list/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#new/2"><c>new/2</c></seemfa>,
+ <seemfa marker="#to_list/1"><c>to_list/1</c></seemfa>.</p>
</desc>
</func>
@@ -211,9 +211,9 @@ A3 = array:fix(A2).</pre>
<name name="from_orddict" arity="1" since=""/>
<fsummary>Equivalent to <c>from_orddict(Orddict, undefined)</c>.
</fsummary>
- <desc><marker id="from_orddict-1"/>
+ <desc>
<p>Equivalent to
- <seealso marker="#from_orddict-2"><c>from_orddict(<anno>Orddict</anno>, undefined)</c></seealso>.</p>
+ <seemfa marker="#from_orddict/2"><c>from_orddict(<anno>Orddict</anno>, undefined)</c></seemfa>.</p>
</desc>
</func>
@@ -221,22 +221,22 @@ A3 = array:fix(A2).</pre>
<name name="from_orddict" arity="2" since=""/>
<fsummary>Convert an ordered list of pairs <c>{Index, Value}</c> to a
corresponding extendible array.</fsummary>
- <desc><marker id="from_orddict-2"/>
+ <desc>
<p>Converts an ordered list of pairs <c>{Index, <anno>Value</anno>}</c>
to a 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 non-negative integers, the call fails with
reason <c>badarg</c>.</p>
- <p>See also <seealso marker="#new-2"><c>new/2</c></seealso>,
- <seealso marker="#to_orddict-1"><c>to_orddict/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#new/2"><c>new/2</c></seemfa>,
+ <seemfa marker="#to_orddict/1"><c>to_orddict/1</c></seemfa>.</p>
</desc>
</func>
<func>
<name name="get" arity="2" since=""/>
<fsummary>Get the value of entry <c>I</c>.</fsummary>
- <desc><marker id="get-2"/>
+ <desc>
<p>Gets the value of entry <c><anno>I</anno></c>. If
<c><anno>I</anno></c> is not a non-negative integer, or if the array
has fixed size and <c><anno>I</anno></c> is larger than the maximum
@@ -244,7 +244,7 @@ A3 = array:fix(A2).</pre>
<p>If the array does not have fixed size, the default value for any
index <c><anno>I</anno></c> greater than
<c>size(<anno>Array</anno>)-1</c> is returned.</p>
- <p>See also <seealso marker="#set-3"><c>set/3</c></seealso>.</p>
+ <p>See also <seemfa marker="#set/3"><c>set/3</c></seemfa>.</p>
</desc>
</func>
@@ -252,7 +252,7 @@ A3 = array:fix(A2).</pre>
<name name="is_array" arity="1" since=""/>
<fsummary>Returns <c>true</c> if <c>X</c> is an array, otherwise
<c>false</c>.</fsummary>
- <desc><marker id="is_array-1"/>
+ <desc>
<p>Returns <c>true</c> if <c><anno>X</anno></c> is an array, otherwise
<c>false</c>. Notice that the check is only shallow, as there is no
guarantee that <c><anno>X</anno></c> is a well-formed array
@@ -263,24 +263,24 @@ A3 = array:fix(A2).</pre>
<func>
<name name="is_fix" arity="1" since=""/>
<fsummary>Check if the array has fixed size.</fsummary>
- <desc><marker id="is_fix-1"/>
+ <desc>
<p>Checks if the array has fixed size. Returns <c>true</c> if the array
is fixed, otherwise <c>false</c>.</p>
- <p>See also <seealso marker="#fix-1"><c>fix/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#fix/1"><c>fix/1</c></seemfa>.</p>
</desc>
</func>
<func>
<name name="map" arity="2" since=""/>
<fsummary>Map the specified function onto each array element.</fsummary>
- <desc><marker id="map-2"/>
+ <desc>
<p>Maps the specified function onto each array element. The elements are
visited in order from 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>See also <seealso marker="#foldl-3"><c>foldl/3</c></seealso>,
- <seealso marker="#foldr-3"><c>foldr/3</c></seealso>,
- <seealso marker="#sparse_map-2"><c>sparse_map/2</c></seealso>.</p>
+ <p>See also <seemfa marker="#foldl/3"><c>foldl/3</c></seemfa>,
+ <seemfa marker="#foldr/3"><c>foldr/3</c></seemfa>,
+ <seemfa marker="#sparse_map/2"><c>sparse_map/2</c></seemfa>.</p>
</desc>
</func>
@@ -288,10 +288,10 @@ A3 = array:fix(A2).</pre>
<name name="new" arity="0" since=""/>
<fsummary>Create a new, extendible array with initial size zero.
</fsummary>
- <desc><marker id="new-0"/>
+ <desc>
<p>Creates a new, extendible array with initial size zero.</p>
- <p>See also <seealso marker="#new-1"><c>new/1</c></seealso>,
- <seealso marker="#new-2"><c>new/2</c></seealso>.</p>
+ <p>See also <seemfa marker="#new/1"><c>new/1</c></seemfa>,
+ <seemfa marker="#new/2"><c>new/2</c></seemfa>.</p>
</desc>
</func>
@@ -299,7 +299,7 @@ A3 = array:fix(A2).</pre>
<name name="new" arity="1" since=""/>
<fsummary>Create a new array according to the specified options.
</fsummary>
- <desc><marker id="new-1"/>
+ <desc>
<p>Creates a new array according to the specified otions. By default,
the array is extendible and has initial size zero. Array indices
start at <c>0</c>.</p>
@@ -313,7 +313,7 @@ A3 = array:fix(A2).</pre>
call fails with reason <c>badarg</c>.</p></item>
<tag><c>fixed</c> or <c>{fixed, true}</c></tag>
<item><p>Creates a fixed-size array. See also
- <seealso marker="#fix-1"><c>fix/1</c></seealso>.</p></item>
+ <seemfa marker="#fix/1"><c>fix/1</c></seemfa>.</p></item>
<tag><c>{fixed, false}</c></tag>
<item><p>Creates an extendible (non-fixed-size) array.</p></item>
<tag><c>{default, Value}</c></tag>
@@ -336,12 +336,12 @@ array:new({default,0})</pre>
array:new([{size,10},{fixed,false},{default,-1}])</pre>
<p>creates an extendible array with initial size 10 whose default value
is <c>-1</c>.</p>
- <p>See also <seealso marker="#fix-1"><c>fix/1</c></seealso>,
- <seealso marker="#from_list-2"><c>from_list/2</c></seealso>,
- <seealso marker="#get-2"><c>get/2</c></seealso>,
- <seealso marker="#new-0"><c>new/0</c></seealso>,
- <seealso marker="#new-2"><c>new/2</c></seealso>,
- <seealso marker="#set-3"><c>set/3</c></seealso>.</p>
+ <p>See also <seemfa marker="#fix/1"><c>fix/1</c></seemfa>,
+ <seemfa marker="#from_list/2"><c>from_list/2</c></seemfa>,
+ <seemfa marker="#get/2"><c>get/2</c></seemfa>,
+ <seemfa marker="#new/0"><c>new/0</c></seemfa>,
+ <seemfa marker="#new/2"><c>new/2</c></seemfa>,
+ <seemfa marker="#set/3"><c>set/3</c></seemfa>.</p>
</desc>
</func>
@@ -349,7 +349,7 @@ array:new([{size,10},{fixed,false},{default,-1}])</pre>
<name name="new" arity="2" since=""/>
<fsummary>Create a new array according to the specified size and options.
</fsummary>
- <desc><marker id="new-2"/>
+ <desc>
<p>Creates a new array according to the specified size and options. If
<c><anno>Size</anno></c> is not a non-negative integer, the call fails
with reason <c>badarg</c>. By default, the array has fixed size.
@@ -365,17 +365,17 @@ array:new([{size,10},{fixed,false},{default,-1}])</pre>
array:new(100, {default,0})</pre>
<p>creates a fixed-size array of size 100, whose default value is
<c>0</c>.</p>
- <p>See also <seealso marker="#new-1"><c>new/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#new/1"><c>new/1</c></seemfa>.</p>
</desc>
</func>
<func>
<name name="relax" arity="1" since=""/>
<fsummary>Make the array resizable.</fsummary>
- <desc><marker id="relax-1"/>
+ <desc>
<p>Makes the array resizable. (Reverses the effects of
- <seealso marker="#fix-1"><c>fix/1</c></seealso>.)</p>
- <p>See also <seealso marker="#fix-1"><c>fix/1</c></seealso>.</p>
+ <seemfa marker="#fix/1"><c>fix/1</c></seemfa>.)</p>
+ <p>See also <seemfa marker="#fix/1"><c>fix/1</c></seemfa>.</p>
</desc>
</func>
@@ -383,18 +383,18 @@ array:new(100, {default,0})</pre>
<name name="reset" arity="2" since=""/>
<fsummary>Reset entry <c>I</c> to the default value for the array.
</fsummary>
- <desc><marker id="reset-2"/>
+ <desc>
<p>Resets 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 is returned unchanged. Reset never changes the array
size. Shrinking can be done explicitly by calling
- <seealso marker="#resize-2"><c>resize/2</c></seealso>.</p>
+ <seemfa marker="#resize/2"><c>resize/2</c></seemfa>.</p>
<p>If <c><anno>I</anno></c> is not a non-negative 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>; compare
- <seealso marker="#set-3"><c>set/3</c></seealso></p>
- <p>See also <seealso marker="#new-2"><c>new/2</c></seealso>,
- <seealso marker="#set-3"><c>set/3</c></seealso>.</p>
+ <seemfa marker="#set/3"><c>set/3</c></seemfa></p>
+ <p>See also <seemfa marker="#new/2"><c>new/2</c></seemfa>,
+ <seemfa marker="#set/3"><c>set/3</c></seemfa>.</p>
</desc>
</func>
@@ -402,20 +402,20 @@ array:new(100, {default,0})</pre>
<name name="resize" arity="1" since=""/>
<fsummary>Change the array size to that reported by <c>sparse_size/1</c>.
</fsummary>
- <desc><marker id="resize-1"/>
+ <desc>
<p>Changes the array size to that reported by
- <seealso marker="#sparse_size-1"><c>sparse_size/1</c></seealso>. If
+ <seemfa marker="#sparse_size/1"><c>sparse_size/1</c></seemfa>. If
the specified array has fixed size, also the resulting array has fixed
size.</p>
- <p>See also <seealso marker="#resize-2"><c>resize/2</c></seealso>,
- <seealso marker="#sparse_size-1"><c>sparse_size/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#resize/2"><c>resize/2</c></seemfa>,
+ <seemfa marker="#sparse_size/1"><c>sparse_size/1</c></seemfa>.</p>
</desc>
</func>
<func>
<name name="resize" arity="2" since=""/>
<fsummary>Change the array size.</fsummary>
- <desc><marker id="resize-2"/>
+ <desc>
<p>Change the array size. If <c><anno>Size</anno></c> is not a
non-negative integer, the call fails with reason <c>badarg</c>. If
the specified array has fixed size, also the resulting array has fixed
@@ -426,7 +426,7 @@ array:new(100, {default,0})</pre>
<func>
<name name="set" arity="3" since=""/>
<fsummary>Set entry <c>I</c> of the array to <c>Value</c>.</fsummary>
- <desc><marker id="set-3"/>
+ <desc>
<p>Sets entry <c><anno>I</anno></c> of the array to
<c><anno>Value</anno></c>. If <c><anno>I</anno></c> is not a
non-negative integer, or if the array has fixed size and
@@ -435,21 +435,21 @@ array:new(100, {default,0})</pre>
<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 grows to
size <c><anno>I</anno>+1</c>.</p>
- <p>See also <seealso marker="#get-2"><c>get/2</c></seealso>,
- <seealso marker="#reset-2"><c>reset/2</c></seealso>.</p>
+ <p>See also <seemfa marker="#get/2"><c>get/2</c></seemfa>,
+ <seemfa marker="#reset/2"><c>reset/2</c></seemfa>.</p>
</desc>
</func>
<func>
<name name="size" arity="1" since=""/>
<fsummary>Get the number of entries in the array.</fsummary>
- <desc><marker id="size-1"/>
+ <desc>
<p>Gets the number of entries in the array. Entries are numbered from
<c>0</c> 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>See also <seealso marker="#set-3"><c>set/3</c></seealso>,
- <seealso marker="#sparse_size-1"><c>sparse_size/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#set/3"><c>set/3</c></seemfa>,
+ <seemfa marker="#sparse_size/1"><c>sparse_size/1</c></seemfa>.</p>
</desc>
</func>
@@ -457,14 +457,14 @@ array:new(100, {default,0})</pre>
<name name="sparse_foldl" arity="3" since=""/>
<fsummary>Fold the array elements using the specified function and initial
accumulator value, skipping default-valued entries.</fsummary>
- <desc><marker id="sparse_foldl-3"/>
+ <desc>
<p>Folds the array elements using the specified function and initial
accumulator value, skipping default-valued entries. The elements are
visited in order from 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>See also <seealso marker="#foldl-3"><c>foldl/3</c></seealso>,
- <seealso marker="#sparse_foldr-3"><c>sparse_foldr/3</c></seealso>.</p>
+ <p>See also <seemfa marker="#foldl/3"><c>foldl/3</c></seemfa>,
+ <seemfa marker="#sparse_foldr/3"><c>sparse_foldr/3</c></seemfa>.</p>
</desc>
</func>
@@ -473,14 +473,14 @@ array:new(100, {default,0})</pre>
<fsummary>Fold the array elements right-to-left using the specified
function and initial accumulator value, skipping default-valued
entries.</fsummary>
- <desc><marker id="sparse_foldr-3"/>
+ <desc>
<p>Folds the array elements right-to-left using the specified
function and initial accumulator value, skipping default-valued
entries. The elements are visited in 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>See also <seealso marker="#foldr-3"><c>foldr/3</c></seealso>,
- <seealso marker="#sparse_foldl-3"><c>sparse_foldl/3</c></seealso>.</p>
+ <p>See also <seemfa marker="#foldr/3"><c>foldr/3</c></seemfa>,
+ <seemfa marker="#sparse_foldl/3"><c>sparse_foldl/3</c></seemfa>.</p>
</desc>
</func>
@@ -488,12 +488,12 @@ array:new(100, {default,0})</pre>
<name name="sparse_map" arity="2" since=""/>
<fsummary>Map the specified function onto each array element, skipping
default-valued entries.</fsummary>
- <desc><marker id="sparse_map-2"/>
+ <desc>
<p>Maps the specified function onto each array element, skipping
default-valued entries. The elements are visited in order from 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>See also <seealso marker="#map-2"><c>map/2</c></seealso>.</p>
+ <p>See also <seemfa marker="#map/2"><c>map/2</c></seemfa>.</p>
</desc>
</func>
@@ -501,13 +501,13 @@ array:new(100, {default,0})</pre>
<name name="sparse_size" arity="1" since=""/>
<fsummary>Get the number of entries in the array up until the last
non-default-valued entry.</fsummary>
- <desc><marker id="sparse_size-1"/>
+ <desc>
<p>Gets the number of entries in the array up until the last
non-default-valued entry. That is, returns <c>I+1</c> if <c>I</c> is
the last non-default-valued entry in the array, or zero if no such
entry exists.</p>
- <p>See also <seealso marker="#resize-1"><c>resize/1</c></seealso>,
- <seealso marker="#size-1"><c>size/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#resize/1"><c>resize/1</c></seemfa>,
+ <seemfa marker="#size/1"><c>size/1</c></seemfa>.</p>
</desc>
</func>
@@ -515,9 +515,9 @@ array:new(100, {default,0})</pre>
<name name="sparse_to_list" arity="1" since=""/>
<fsummary>Convert the array to a list, skipping default-valued entries.
</fsummary>
- <desc><marker id="sparse_to_list-1"/>
+ <desc>
<p>Converts the array to a list, skipping default-valued entries.</p>
- <p>See also <seealso marker="#to_list-1"><c>to_list/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#to_list/1"><c>to_list/1</c></seemfa>.</p>
</desc>
</func>
@@ -525,21 +525,21 @@ array:new(100, {default,0})</pre>
<name name="sparse_to_orddict" arity="1" since=""/>
<fsummary>Convert the array to an ordered list of pairs <c>{Index,
Value}</c>, skipping default-valued entries.</fsummary>
- <desc><marker id="sparse_to_orddict-1"/>
+ <desc>
<p>Converts the array to an ordered list of pairs <c>{Index,
<anno>Value</anno>}</c>, skipping default-valued entries.</p>
<p>See also
- <seealso marker="#to_orddict-1"><c>to_orddict/1</c></seealso>.</p>
+ <seemfa marker="#to_orddict/1"><c>to_orddict/1</c></seemfa>.</p>
</desc>
</func>
<func>
<name name="to_list" arity="1" since=""/>
<fsummary>Convert the array to a list.</fsummary>
- <desc><marker id="to_list-1"/>
+ <desc>
<p>Converts the array to a list.</p>
- <p>See also <seealso marker="#from_list-2"><c>from_list/2</c></seealso>,
- <seealso marker="#sparse_to_list-1"><c>sparse_to_list/1</c></seealso>.
+ <p>See also <seemfa marker="#from_list/2"><c>from_list/2</c></seemfa>,
+ <seemfa marker="#sparse_to_list/1"><c>sparse_to_list/1</c></seemfa>.
</p>
</desc>
</func>
@@ -548,12 +548,12 @@ array:new(100, {default,0})</pre>
<name name="to_orddict" arity="1" since=""/>
<fsummary>Convert the array to an ordered list of pairs <c>{Index,
Value}</c>.</fsummary>
- <desc><marker id="to_orddict-1"/>
+ <desc>
<p>Converts the array to an ordered list of pairs <c>{Index,
<anno>Value</anno>}</c>.</p>
<p>See also
- <seealso marker="#from_orddict-2"><c>from_orddict/2</c></seealso>,
- <seealso marker="#sparse_to_orddict-1"><c>sparse_to_orddict/1</c></seealso>.
+ <seemfa marker="#from_orddict/2"><c>from_orddict/2</c></seemfa>,
+ <seemfa marker="#sparse_to_orddict/1"><c>sparse_to_orddict/1</c></seemfa>.
</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/assert_hrl.xml b/lib/stdlib/doc/src/assert_hrl.xml
index fb27954235..c08066d7fb 100644
--- a/lib/stdlib/doc/src/assert_hrl.xml
+++ b/lib/stdlib/doc/src/assert_hrl.xml
@@ -185,7 +185,7 @@ erlc -DNOASSERT=true *.erl</code>
<section>
<title>See Also</title>
- <p><seealso marker="compiler:compile"><c>compile(3)</c></seealso>,
- <seealso marker="erts:erlc"><c>erlc(3)</c></seealso></p>
+ <p><seeerl marker="compiler:compile"><c>compile(3)</c></seeerl>,
+ <seecom marker="erts:erlc"><c>erlc(3)</c></seecom></p>
</section>
</fileref>
diff --git a/lib/stdlib/doc/src/beam_lib.xml b/lib/stdlib/doc/src/beam_lib.xml
index 91e5a6d14a..a33c5b076c 100644
--- a/lib/stdlib/doc/src/beam_lib.xml
+++ b/lib/stdlib/doc/src/beam_lib.xml
@@ -59,9 +59,9 @@
<marker id="debug_info"></marker>
<title>Debug Information/Abstract Code</title>
<p>Option <c>debug_info</c> can be specified to the Compiler (see
- <seealso marker="compiler:compile#debug_info"><c>compile(3)</c></seealso>)
- to have debug information, such as <seealso marker="erts:absform">Erlang
- Abstract Format</seealso>, stored in the <c>debug_info</c> chunk.
+ <seeerl marker="compiler:compile#debug_info"><c>compile(3)</c></seeerl>)
+ to have debug information, such as <seeguide marker="erts:absform">Erlang
+ Abstract Format</seeguide>, stored in the <c>debug_info</c> chunk.
Tools such as Debugger and Xref require the debug information to
be included.</p>
@@ -71,9 +71,9 @@
</warning>
<p>The debug information can also be removed from BEAM files
- using <seealso marker="#strip/1"><c>strip/1</c></seealso>,
- <seealso marker="#strip_files/1"><c>strip_files/1</c></seealso>, and/or
- <seealso marker="#strip_release/1"><c>strip_release/1</c></seealso>.</p>
+ using <seemfa marker="#strip/1"><c>strip/1</c></seemfa>,
+ <seemfa marker="#strip_files/1"><c>strip_files/1</c></seemfa>, and/or
+ <seemfa marker="#strip_release/1"><c>strip_release/1</c></seemfa>.</p>
</section>
<section>
@@ -101,7 +101,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code>
<p>The default type (and currently the only type) of crypto
algorithm is <c>des3_cbc</c>, three rounds of DES. The key string
is scrambled using
- <seealso marker="erts:erlang#md5/1"><c>erlang:md5/1</c></seealso>
+ <seemfa marker="erts:erlang#md5/1"><c>erlang:md5/1</c></seemfa>
to generate the keys used for <c>des3_cbc</c>.</p>
<note>
@@ -117,9 +117,9 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code>
<list type="ordered">
<item>
<p>Use Compiler option <c>{debug_info_key,Key}</c>, see
- <seealso marker="compiler:compile#debug_info_key"><c>compile(3)</c></seealso>
+ <seeerl marker="compiler:compile#debug_info_key"><c>compile(3)</c></seeerl>
and function
- <seealso marker="#crypto_key_fun/1"><c>crypto_key_fun/1</c></seealso>
+ <seemfa marker="#crypto_key_fun/1"><c>crypto_key_fun/1</c></seemfa>
to register a fun that returns the key whenever
<c>beam_lib</c> must decrypt the debug information.</p>
<p>If no such fun is registered, <c>beam_lib</c> instead
@@ -129,7 +129,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code>
<p>Store the key in a text file named <c>.erlang.crypt</c>.</p>
<p>In this case, Compiler option <c>encrypt_debug_info</c>
can be used, see
- <seealso marker="compiler:compile#encrypt_debug_info"><c>compile(3)</c></seealso>.
+ <seeerl marker="compiler:compile#encrypt_debug_info"><c>compile(3)</c></seeerl>.
</p>
</item>
</list>
@@ -317,7 +317,7 @@ io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).</code>
<desc>
<p>Unregisters the crypto key fun and terminates the process
holding it, started by
- <seealso marker="#crypto_key_fun/1"><c>crypto_key_fun/1</c></seealso>.
+ <seemfa marker="#crypto_key_fun/1"><c>crypto_key_fun/1</c></seemfa>.
</p>
<p>Returns either <c>{ok, undefined}</c> if no crypto key fun is
registered, or <c>{ok, Term}</c>, where <c>Term</c> is
@@ -402,7 +402,7 @@ CryptoKeyFun(clear) -> term()</code>
<fsummary>Compare the BEAM files in two directories.</fsummary>
<desc>
<p>Compares the BEAM files in two directories as
- <seealso marker="#cmp_dirs/2"><c>cmp_dirs/2</c></seealso>, but the
+ <seemfa marker="#cmp_dirs/2"><c>cmp_dirs/2</c></seemfa>, but the
names of files that exist in only one directory or are different are
presented on standard output.</p>
</desc>
@@ -416,7 +416,7 @@ CryptoKeyFun(clear) -> term()</code>
<p>For a specified error returned by any function in this module,
this function returns a descriptive string
of the error in English. For file errors, function
- <seealso marker="kernel:file#format_error/1"><c>file:format_error(Posix)</c></seealso>
+ <seemfa marker="kernel:file#format_error/1"><c>file:format_error(Posix)</c></seemfa>
is to be called.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml
index 2dd6c6a436..412a0f5def 100644
--- a/lib/stdlib/doc/src/binary.xml
+++ b/lib/stdlib/doc/src/binary.xml
@@ -64,7 +64,7 @@
</datatype>
<datatype>
<name name="part"/>
- <desc><p>A representaion of a part (or range) in a binary. <c>Start</c> is
+ <desc><p>A representation of a part (or range) in a binary. <c>Start</c> is
a zero-based offset into a <c>binary()</c> and <c>Length</c> is the
length of that part. As input to functions in this module, a reverse
part specification is allowed, constructed with a negative
@@ -131,10 +131,10 @@
<desc>
<p>Builds an internal structure representing a compilation of a
search pattern, later to be used in functions
- <seealso marker="#match-3"><c>match/3</c></seealso>,
- <seealso marker="#matches-3"><c>matches/3</c></seealso>,
- <seealso marker="#split-3"><c>split/3</c></seealso>, or
- <seealso marker="#replace-4"><c>replace/4</c></seealso>.
+ <seemfa marker="#match/3"><c>match/3</c></seemfa>,
+ <seemfa marker="#matches/3"><c>matches/3</c></seemfa>,
+ <seemfa marker="#split/3"><c>split/3</c></seemfa>, or
+ <seemfa marker="#replace/4"><c>replace/4</c></seemfa>.
The <c>cp()</c> returned is guaranteed to be a
<c>tuple()</c> to allow programs to distinguish it from
non-precompiled search patterns.</p>
@@ -173,7 +173,7 @@
duplicated <c><anno>N</anno></c> times.</p>
<p>This function always creates a new binary, even if <c><anno>N</anno> =
- 1</c>. By using <seealso marker="#copy/1"><c>copy/1</c></seealso>
+ 1</c>. By using <seemfa marker="#copy/1"><c>copy/1</c></seemfa>
on a binary referencing a larger binary, one
can free up the larger binary for garbage collection.</p>
@@ -267,7 +267,7 @@
<fsummary>Convert a list of integers and binaries to a binary.</fsummary>
<desc>
<p>Works exactly as
- <seealso marker="erts:erlang#list_to_binary/1"><c>erlang:list_to_binary/1</c></seealso>,
+ <seemfa marker="erts:erlang#list_to_binary/1"><c>erlang:list_to_binary/1</c></seemfa>,
added for completeness.</p>
</desc>
</func>
@@ -361,7 +361,7 @@
atom <c>nomatch</c> is returned.</p>
<p>For a description of <c><anno>Pattern</anno></c>, see function
- <seealso marker="#compile_pattern-1"><c>compile_pattern/1</c></seealso>.
+ <seemfa marker="#compile_pattern/1"><c>compile_pattern/1</c></seemfa>.
</p>
<p>If <c>{scope, {Start,Length}}</c> is specified in the options such
@@ -385,7 +385,7 @@
<fsummary>Search for all matches of a pattern in a binary.</fsummary>
<type name="part"/>
<desc>
- <p>As <seealso marker="#match-2"><c>match/2</c></seealso>,
+ <p>As <seemfa marker="#match/2"><c>match/2</c></seemfa>,
but <c><anno>Subject</anno></c> is searched until
exhausted and a list of all non-overlapping parts matching
<c><anno>Pattern</anno></c> is returned (in order).</p>
@@ -411,9 +411,9 @@
returned.</p>
<p>For a description of <c><anno>Pattern</anno></c>, see
- <seealso marker="#compile_pattern-1"><c>compile_pattern/1</c></seealso>.
+ <seemfa marker="#compile_pattern/1"><c>compile_pattern/1</c></seemfa>.
For a description of available options, see
- <seealso marker="#match-3"><c>match/3</c></seealso>.</p>
+ <seemfa marker="#match/3"><c>match/3</c></seemfa>.</p>
<p>If <c>{scope, {<anno>Start</anno>,<anno>Length</anno>}}</c> is
specified in the options such that <c><anno>Start</anno></c> &gt; size
@@ -440,9 +440,9 @@
&lt;&lt;6,7,8,9,10&gt;&gt;</code>
<note>
- <p><seealso marker="#part-2">part/2</seealso> and
- <seealso marker="#part-3">part/3</seealso> are also available in the
- <seealso marker="erts:erlang"><c>erlang</c></seealso>
+ <p><seemfa marker="#part/2">part/2</seemfa> and
+ <seemfa marker="#part/3">part/3</seemfa> are also available in the
+ <seeerl marker="erts:erlang"><c>erlang</c></seeerl>
module under the names <c>binary_part/2</c> and
<c>binary_part/3</c>. Those BIFs are allowed in guard tests.</p>
</note>
@@ -469,7 +469,7 @@
<p>If a binary references a larger binary (often described as
being a subbinary), it can be useful to get the size of the
referenced binary. This function can be used in a program to trigger the
- use of <seealso marker="#copy/1"><c>copy/1</c></seealso>. By copying a
+ use of <seemfa marker="#copy/1"><c>copy/1</c></seemfa>. By copying a
binary, one can dereference the original, possibly large, binary that a
smaller binary is a reference to.</p>
@@ -566,11 +566,11 @@ store(Binary, GBSet) ->
of the replacement binary, a <c>badarg</c> exception is raised.</p>
<p>Options <c>global</c> and <c>{scope, part()}</c> work as for
- <seealso marker="#split-3"><c>split/3</c></seealso>.
+ <seemfa marker="#split/3"><c>split/3</c></seemfa>.
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"><c>compile_pattern/1</c></seealso>.
+ <seemfa marker="#compile_pattern/1"><c>compile_pattern/1</c></seemfa>.
</p>
</desc>
</func>
@@ -608,23 +608,23 @@ store(Binary, GBSet) ->
<taglist>
<tag>{scope, part()}</tag>
- <item><p>Works as in <seealso marker="#match-3"><c>match/3</c></seealso>
- and <seealso marker="#matches-3"><c>matches/3</c></seealso>. Notice that
+ <item><p>Works as in <seemfa marker="#match/3"><c>match/3</c></seemfa>
+ and <seemfa marker="#matches/3"><c>matches/3</c></seemfa>. Notice 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 are kept in the result. See the example below.</p></item>
<tag>trim</tag>
<item><p>Removes trailing empty parts of the result (as does <c>trim</c>
- in <seealso marker="re#split/3"><c>re:split/3</c></seealso>.</p></item>
+ in <seemfa marker="re#split/3"><c>re:split/3</c></seemfa>.</p></item>
<tag>trim_all</tag>
<item><p>Removes all empty parts of the result.</p></item>
<tag>global</tag>
<item><p>Repeats the split until <c><anno>Subject</anno></c> is
exhausted. Conceptually option <c>global</c> makes split work
on the positions returned by
- <seealso marker="#matches-3"><c>matches/3</c></seealso>, while it
+ <seemfa marker="#matches/3"><c>matches/3</c></seemfa>, while it
normally works on the position returned by
- <seealso marker="#match-3"><c>match/3</c></seealso>.</p></item>
+ <seemfa marker="#match/3"><c>match/3</c></seemfa>.</p></item>
</taglist>
<p>Example of the difference between a scope and taking the
@@ -643,7 +643,7 @@ store(Binary, GBSet) ->
of the split are no longer referenced.</p>
<p>For a description of <c><anno>Pattern</anno></c>, see
- <seealso marker="#compile_pattern-1"><c>compile_pattern/1</c></seealso>.
+ <seemfa marker="#compile_pattern/1"><c>compile_pattern/1</c></seemfa>.
</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/c.xml b/lib/stdlib/doc/src/c.xml
index 29edc373c7..b481596379 100644
--- a/lib/stdlib/doc/src/c.xml
+++ b/lib/stdlib/doc/src/c.xml
@@ -63,8 +63,8 @@
source file, then the code path is searched to locate the object
file for the module and extract its original compiler options and
source path. If the source file is not found in the original
- location, <seealso
- marker="filelib#find_source/1"><c>filelib:find_source/1</c></seealso>
+ location, <seemfa
+ marker="filelib#find_source/1"><c>filelib:find_source/1</c></seemfa>
is used to search for it relative to the directory of the object
file.</p>
<p>The source file is compiled with the the original
@@ -120,6 +120,64 @@
</func>
<func>
+ <name name="h" arity="1" since="OTP 23.0"/>
+ <fsummary>Module help information</fsummary>
+ <type name="h_return"/>
+ <desc>
+ <p>Print the documentation for <c>Module</c></p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="h" arity="2" since="OTP 23.0"/>
+ <fsummary>Function help information</fsummary>
+ <type name="h_return"/>
+ <type name="hf_return"/>
+ <desc>
+ <p>Print the documentation for all <c>Module:Function</c>s (regardless of arity).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="h" arity="3" since="OTP 23.0"/>
+ <fsummary>Function help information</fsummary>
+ <type name="h_return"/>
+ <type name="hf_return"/>
+ <desc>
+ <p>Print the documentation for <c>Module:Function/Arity</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="ht" arity="1" since="OTP 23.0"/>
+ <fsummary>Type help information</fsummary>
+ <type name="h_return"/>
+ <desc>
+ <p>Print the type documentation for <c>Module</c></p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="ht" arity="2" since="OTP 23.0"/>
+ <fsummary>Type help information</fsummary>
+ <type name="h_return"/>
+ <type name="ht_return"/>
+ <desc>
+ <p>Print the type documentation for <c>Type</c> in <c>Module</c> regardless of arity.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="ht" arity="3" since="OTP 23.0"/>
+ <fsummary>Type help information</fsummary>
+ <type name="h_return"/>
+ <type name="ht_return"/>
+ <desc>
+ <p>Print the type documentation for <c>Type/Arity</c> in <c>Module</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="i" arity="0" since=""/>
<name name="ni" arity="0" since=""/>
<fsummary>System information.</fsummary>
@@ -165,7 +223,7 @@
<c>compile:file(File, [report_errors, report_warnings])</c> for each
<c>File</c> in <c>Files</c>.</p>
<p>For information about <c>File</c>, see
- <seealso marker="file#type-filename"><c>file:filename()</c></seealso>.
+ <seetype marker="file#filename"><c>file:filename()</c></seetype>.
</p>
</desc>
</func>
@@ -218,7 +276,7 @@
<fsummary>Lists all modified modules.</fsummary>
<desc>
<p>Lists all modified modules. Shorthand for
- <seealso marker="kernel:code#modified_modules/0"><c>code:modified_modules/0</c></seealso>.</p>
+ <seemfa marker="kernel:code#modified_modules/0"><c>code:modified_modules/0</c></seemfa>.</p>
</desc>
</func>
@@ -227,7 +285,7 @@
<fsummary>Memory allocation information.</fsummary>
<desc>
<p>Memory allocation information. Equivalent to
- <seealso marker="erts:erlang#memory/0"><c>erlang:memory/0</c></seealso>.</p>
+ <seemfa marker="erts:erlang#memory/0"><c>erlang:memory/0</c></seemfa>.</p>
</desc>
</func>
@@ -237,7 +295,7 @@
<fsummary>Memory allocation information.</fsummary>
<desc>
<p>Memory allocation information. Equivalent to
- <seealso marker="erts:erlang#memory/1"><c>erlang:memory/1</c></seealso>.</p>
+ <seemfa marker="erts:erlang#memory/1"><c>erlang:memory/1</c></seemfa>.</p>
</desc>
</func>
@@ -336,9 +394,9 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w
<code type="none">
yecc:file(File)</code>
<p>For information about <c>File = name()</c>, see
- <seealso marker="filename"><c>filename(3)</c></seealso>.
+ <seeerl marker="filename"><c>filename(3)</c></seeerl>.
For information about <c>YeccRet</c>, see
- <seealso marker="parsetools:yecc#file/1"><c>yecc:file/2</c></seealso>.
+ <seemfa marker="parsetools:yecc#file/1"><c>yecc:file/2</c></seemfa>.
</p>
</desc>
</func>
@@ -355,9 +413,9 @@ yecc:file(File)</code>
<code type="none">
yecc:file(File, Options)</code>
<p>For information about <c>File = name()</c>, see
- <seealso marker="filename"><c>filename(3)</c></seealso>.
+ <seeerl marker="filename"><c>filename(3)</c></seeerl>.
For information about <c>Options</c> and <c>YeccRet</c>, see
- <seealso marker="parsetools:yecc#file/1"><c>yecc:file/2</c></seealso>.
+ <seemfa marker="parsetools:yecc#file/1"><c>yecc:file/2</c></seemfa>.
</p>
</desc>
</func>
@@ -365,11 +423,11 @@ yecc:file(File, Options)</code>
<section>
<title>See Also</title>
- <p><seealso marker="filename"><c>filename(3)</c></seealso>,
- <seealso marker="compiler:compile"><c>compile(3)</c></seealso>,
- <seealso marker="erts:erlang"><c>erlang(3)</c></seealso>,
- <seealso marker="parsetools:yecc"><c>yecc(3)</c></seealso>,
- <seealso marker="tools:xref"><c>xref(3)</c></seealso></p>
+ <p><seeerl marker="filename"><c>filename(3)</c></seeerl>,
+ <seeerl marker="compiler:compile"><c>compile(3)</c></seeerl>,
+ <seeerl marker="erts:erlang"><c>erlang(3)</c></seeerl>,
+ <seeerl marker="parsetools:yecc"><c>yecc(3)</c></seeerl>,
+ <seeerl marker="tools:xref"><c>xref(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml
index 6cc0befd45..213bca365d 100644
--- a/lib/stdlib/doc/src/calendar.xml
+++ b/lib/stdlib/doc/src/calendar.xml
@@ -41,7 +41,7 @@
Greenwich Mean Time (GMT).</p>
<p>The time functions <c>local_time/0</c> and
<c>universal_time/0</c> in this module both return date
- and time. The is because separate functions for date
+ and time. This is because separate functions for date
and time can result in a date/time combination that is displaced
by 24 hours. This occurs if one of the functions is called
before midnight, and the other after midnight. This problem also
@@ -201,7 +201,7 @@
<p>Returns tuple <c>{Year, WeekNum}</c> representing
the ISO week number for the actual date. To determine the
actual date, use function
- <seealso marker="#local_time/0"><c>local_time/0</c></seealso>.</p>
+ <seemfa marker="#local_time/0"><c>local_time/0</c></seemfa>.</p>
</desc>
</func>
@@ -241,8 +241,8 @@
date after Jan 1, 1970.</p>
<warning>
<p>This function is deprecated. Use
- <seealso marker="#local_time_to_universal_time_dst/1">
- <c>local_time_to_universal_time_dst/1</c></seealso>
+ <seemfa marker="#local_time_to_universal_time_dst/1">
+ <c>local_time_to_universal_time_dst/1</c></seemfa>
instead, as it gives a more correct and complete result.
Especially for
the period that does not exist, as it is skipped during
@@ -290,7 +290,7 @@
<desc>
<p>Returns Universal Coordinated Time (UTC)
converted from the return value from
- <seealso marker="erts:erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso>.
+ <seemfa marker="erts:erlang#timestamp/0"><c>erlang:timestamp/0</c></seemfa>.
</p>
</desc>
</func>
@@ -300,7 +300,7 @@
<fsummary>Convert now to local date and time.</fsummary>
<desc>
<p>Returns local date and time converted from the return value from
- <seealso marker="erts:erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso>.
+ <seemfa marker="erts:erlang#timestamp/0"><c>erlang:timestamp/0</c></seemfa>.
</p>
</desc>
</func>
@@ -311,7 +311,7 @@
<desc>
<p>Returns Universal Coordinated Time (UTC)
converted from the return value from
- <seealso marker="erts:erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso>.
+ <seemfa marker="erts:erlang#timestamp/0"><c>erlang:timestamp/0</c></seemfa>.
</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml
index ba319cf8ff..873167714c 100644
--- a/lib/stdlib/doc/src/dets.xml
+++ b/lib/stdlib/doc/src/dets.xml
@@ -84,7 +84,7 @@
is important to realize that a single look-up operation involves a
series of disk seek and read operations. The Dets functions
are therefore much slower than the corresponding
- <seealso marker="ets"><c>ets(3)</c></seealso> functions,
+ <seeerl marker="ets"><c>ets(3)</c></seeerl> functions,
although Dets exports a similar interface.</p>
<p>Dets organizes data as a linear hash list and the hash list
@@ -107,8 +107,8 @@
ordered disk-based term storage.</p>
<p>All Dets functions return <c>{error, Reason}</c> if an error
- occurs (<seealso marker="#first/1"><c>first/1</c></seealso> and
- <seealso marker="#next/2"><c>next/2</c></seealso> are exceptions, they
+ occurs (<seemfa marker="#first/1"><c>first/1</c></seemfa> and
+ <seemfa marker="#next/2"><c>next/2</c></seemfa> are exceptions, they
exit the process with the error tuple). If badly formed arguments are
specified, all functions exit the process with a <c>badarg</c>
message.</p>
@@ -124,16 +124,16 @@
<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>
+ <p>Opaque continuation used by <seemfa marker="#match/1">
+ <c>match/1</c></seemfa> and <seemfa marker="#match/3">
+ <c>match/3</c></seemfa>.</p>
</desc>
</datatype>
<datatype>
<name name="cont"/>
<desc>
- <p>Opaque continuation used by <seealso marker="#bchunk/2">
- <c>bchunk/2</c></seealso>.</p>
+ <p>Opaque continuation used by <seemfa marker="#bchunk/2">
+ <c>bchunk/2</c></seemfa>.</p>
</desc>
</datatype>
<datatype>
@@ -143,9 +143,9 @@
<name name="match_spec"/>
<desc>
<p>Match specifications, see section
- <seealso marker="erts:match_spec">
- Match Specification in Erlang</seealso> in ERTS User's Guide and the
- <seealso marker="ms_transform"><c>ms_transform(3)</c></seealso>
+ <seeguide marker="erts:match_spec">
+ Match Specification in Erlang</seeguide> in ERTS User's Guide and the
+ <seeerl marker="ms_transform"><c>ms_transform(3)</c></seeerl>
module.</p>
</desc>
</datatype>
@@ -158,24 +158,24 @@
<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>
+ <p>Opaque continuation used by <seemfa marker="#match_object/1">
+ <c>match_object/1</c></seemfa> and
+ <seemfa marker="#match_object/3"><c>match_object/3</c></seemfa>.</p>
</desc>
</datatype>
<datatype>
<name name="pattern"/>
<desc>
<p>For a description of patterns, see
- <seealso marker="ets#match/2"><c>ets:match/2</c></seealso>.</p>
+ <seemfa marker="ets#match/2"><c>ets:match/2</c></seemfa>.</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>
+ <p>Opaque continuation used by <seemfa marker="#select/1">
+ <c>select/1</c></seemfa> and <seemfa marker="#select/3">
+ <c>select/3</c></seemfa>.</p>
</desc>
</datatype>
<datatype>
@@ -205,7 +205,7 @@
representation of the returned objects is not public. The
lists of data can be used for initializing a table by specifying
value <c>bchunk</c> to option <c>format</c> of function
- <seealso marker="#init_table/3"><c>init_table/3</c></seealso>
+ <seemfa marker="#init_table/3"><c>init_table/3</c></seemfa>
The Mnesia application uses this
function for copying open tables.</p>
<p>Unless the table is protected using <c>safe_fixtable/2</c>,
@@ -277,7 +277,7 @@
according to the internal order of the table, 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 <seealso marker="#next/2"><c>next/2</c></seealso>
+ subsequent calls to <seemfa marker="#next/2"><c>next/2</c></seemfa>
do possibly not work as expected if
concurrent updates are made to the table.</p>
<p>If an error occurs, the process is exited with an error
@@ -287,9 +287,9 @@
are not to be used: they are not efficient, and they
prevent the use of key <c>'$end_of_table'</c>, as this atom
is used to indicate the end of the table. If possible, use functions
- <seealso marker="#match/1"><c>match</c></seealso>,
- <seealso marker="#match_object/1"><c>match_object</c></seealso>, and
- <seealso marker="#select/1"><c>select</c></seealso>
+ <seemfa marker="#match/1"><c>match</c></seemfa>,
+ <seemfa marker="#match_object/1"><c>match_object</c></seemfa>, and
+ <seemfa marker="#select/1"><c>select</c></seemfa>
for traversing tables.</p>
</desc>
</func>
@@ -333,21 +333,21 @@
bytes.</p>
</item>
<item>
- <p><c>{filename, </c><seealso marker="file#type-name">
- <c>file:name()</c></seealso><c>}</c> - The name of the file
+ <p><c>{filename, </c><seetype marker="file#name">
+ <c>file:name()</c></seetype><c>}</c> - The name of the file
where objects are stored.</p>
</item>
<item>
- <p><c>{keypos, </c><seealso marker="#type-keypos">
- <c>keypos()</c></seealso><c>}</c> - The key position.</p>
+ <p><c>{keypos, </c><seetype marker="#keypos">
+ <c>keypos()</c></seetype><c>}</c> - The key position.</p>
</item>
<item>
<p><c>{size, integer() >= 0}</c> - The number of objects
stored in the table.</p>
</item>
<item>
- <p><c>{type, </c><seealso marker="#type-type">
- <c>type()</c></seealso><c>}</c> - The table type.</p>
+ <p><c>{type, </c><seetype marker="#type">
+ <c>type()</c></seetype><c>}</c> - The table type.</p>
</item>
</list>
</desc>
@@ -361,16 +361,16 @@
<p>Returns the information associated with <c><anno>Item</anno></c>
for table <c><anno>Name</anno></c>.
In addition to the <c>{<anno>Item</anno>, <anno>Value</anno>}</c>
- pairs defined for <seealso marker="#info/1"><c>info/1</c></seealso>,
+ pairs defined for <seemfa marker="#info/1"><c>info/1</c></seemfa>,
the following items are allowed:</p>
<list type="bulleted">
<item>
- <p><c>{access, </c><seealso marker="#type-access">
- <c>access()</c></seealso><c>}</c> - The access mode.</p>
+ <p><c>{access, </c><seetype marker="#access">
+ <c>access()</c></seetype><c>}</c> - The access mode.</p>
</item>
<item>
- <p><c>{auto_save, </c><seealso marker="#type-auto_save">
- <c>auto_save()</c></seealso><c>}</c> - The autosave interval.</p>
+ <p><c>{auto_save, </c><seetype marker="#auto_save">
+ <c>auto_save()</c></seetype><c>}</c> - The autosave interval.</p>
</item>
<item>
<p><c>{bchunk_format, binary()}</c> - An opaque binary
@@ -429,25 +429,25 @@
There can be any number of processes in the list. If the table
is not fixed, <c>SafeFixed</c> is the atom <c>false</c>.</p>
<p><c>FixedAtTime</c> corresponds to the result returned by
- <seealso marker="erts:erlang#monotonic_time/0">
- <c>erlang:monotonic_time/0</c></seealso> at the time of fixation.
+ <seemfa marker="erts:erlang#monotonic_time/0">
+ <c>erlang:monotonic_time/0</c></seemfa> at the time of fixation.
The use of <c>safe_fixed_monotonic_time</c> is
- <seealso marker="erts:time_correction#Time_Warp_Safe_Code">
- time warp safe</seealso>.</p>
+ <seeguide marker="erts:time_correction#Time_Warp_Safe_Code">
+ time warp safe</seeguide>.</p>
</item>
<item>
<p><c>{safe_fixed, SafeFixed}</c> - The same as
<c>{safe_fixed_monotonic_time, SafeFixed}</c> except
the format and value of <c>FixedAtTime</c>.</p>
<p><c>FixedAtTime</c> corresponds to the result returned by
- <seealso marker="erts:erlang#timestamp/0">
- <c>erlang:timestamp/0</c></seealso> at the time of fixation.
+ <seemfa marker="erts:erlang#timestamp/0">
+ <c>erlang:timestamp/0</c></seemfa> at the time of fixation.
Notice that when the system uses single or multi
- <seealso marker="erts:time_correction#Time_Warp_Modes">time warp
- modes</seealso>, this can produce strange results. This is
+ <seeguide marker="erts:time_correction#Time_Warp_Modes">time warp
+ modes</seeguide>, this can produce strange results. This is
because the use of <c>safe_fixed</c> is not
- <seealso marker="erts:time_correction#Time_Warp_Safe_Code">
- time warp safe</seealso>. Time warp safe code must use
+ <seeguide marker="erts:time_correction#Time_Warp_Safe_Code">
+ time warp safe</seeguide>. Time warp safe code must use
<c>safe_fixed_monotonic_time</c> instead.</p>
</item>
</list>
@@ -508,7 +508,7 @@
<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>.
+ <seemfa marker="#bchunk/2"><c>bchunk/2</c></seemfa>.
This option overrides option <c>min_no_slots</c>.</p>
</item>
</list>
@@ -544,9 +544,9 @@
<desc>
<p>Returns <c>true</c> if it would be possible to initialize
table <c><anno>Name</anno></c>, using
- <seealso marker="#init_table/3"><c>init_table/3</c></seealso> with
+ <seemfa marker="#init_table/3"><c>init_table/3</c></seemfa> with
option <c>{format,&nbsp;bchunk}</c>, with objects read with
- <seealso marker="#bchunk/2"><c>bchunk/2</c></seealso> from some
+ <seemfa marker="#bchunk/2"><c>bchunk/2</c></seemfa> from some
table <c>T</c>, such that calling
<c>info(T,&nbsp;bchunk_format)</c> returns
<c>BchunkFormat</c>.</p>
@@ -613,7 +613,7 @@ ok
<p>Returns for each object of table <c><anno>Name</anno></c> that
matches <c><anno>Pattern</anno></c> a list of bindings in some
unspecified order. For a description of patterns, see
- <seealso marker="ets#match/2"><c>ets:match/2</c></seealso>.
+ <seemfa marker="ets#match/2"><c>ets:match/2</c></seemfa>.
If the keypos'th element of
<c><anno>Pattern</anno></c> is unbound, all table objects are
matched. If the keypos'th element is bound, only the
@@ -630,12 +630,12 @@ ok
returns a non-empty list of the bindings that match
<c><anno>Pattern</anno></c> in some unspecified order.
For a description of patterns, see
- <seealso marker="ets#match/2"><c>ets:match/2</c></seealso>.</p>
+ <seemfa marker="ets#match/2"><c>ets:match/2</c></seemfa>.</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
- <seealso marker="#match/1"><c>match/1</c></seealso>.</p>
+ <seemfa marker="#match/1"><c>match/1</c></seemfa>.</p>
<p>If the keypos'th element of <c><anno>Pattern</anno></c> is bound,
all table objects are matched. If the keypos'th element is
unbound, all table objects are matched, <c><anno>N</anno></c>
@@ -647,7 +647,7 @@ ok
same key are always matched at the same time, which implies that
more than <anno>N</anno> objects can sometimes be matched.</p>
<p>The table is always to be protected using
- <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>
+ <seemfa marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seemfa>
before calling <c>match/3</c>, otherwise
errors can occur when calling <c>match/1</c>.</p>
</desc>
@@ -660,7 +660,7 @@ ok
<desc>
<p>Deletes all objects that match <c><anno>Pattern</anno></c> from
table <c><anno>Name</anno></c>. For a description of patterns,
- see <seealso marker="ets#match/2"><c>ets:match/2</c></seealso>.</p>
+ see <seemfa marker="ets#match/2"><c>ets:match/2</c></seemfa>.</p>
<p>If the keypos'th element of <c>Pattern</c> is bound,
only the objects with the correct key are matched.</p>
</desc>
@@ -690,7 +690,7 @@ ok
<p>Returns a list of all objects of table <c><anno>Name</anno></c> that
match <c><anno>Pattern</anno></c> in some unspecified order.
For a description of patterns, see
- <seealso marker="ets#match/2"><c>ets:match/2</c></seealso>.</p>
+ <seemfa marker="ets#match/2"><c>ets:match/2</c></seemfa>.</p>
<p>If the keypos'th element of <c><anno>Pattern</anno></c> is
unbound, all table objects are matched. If the
keypos'th element of <c><anno>Pattern</anno></c> is bound, only the
@@ -710,12 +710,12 @@ ok
and returns a non-empty list of the objects that match
<c><anno>Pattern</anno></c> in some unspecified order.
For a description of patterns, see
- <seealso marker="ets#match/2"><c>ets:match/2</c></seealso>.</p>
+ <seemfa marker="ets#match/2"><c>ets:match/2</c></seemfa>.</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
- <seealso marker="#match_object/1"><c>match_object/1</c></seealso>.</p>
+ <seemfa marker="#match_object/1"><c>match_object/1</c></seemfa>.</p>
<p>If the keypos'th element of <c><anno>Pattern</anno></c> is bound,
all table objects are matched. If the keypos'th element is
unbound, all table objects are matched, <c><anno>N</anno></c>
@@ -728,7 +728,7 @@ ok
in the same reply, which implies
that more than <anno>N</anno> objects can sometimes be returned.</p>
<p>The table is always to be protected using
- <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>
+ <seemfa marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seemfa>
before calling <c>match_object/3</c>, otherwise
errors can occur when calling <c>match_object/1</c>.</p>
</desc>
@@ -738,7 +738,7 @@ ok
<name name="member" arity="2" since=""/>
<fsummary>Test for occurrence of a key in a Dets table.</fsummary>
<desc>
- <p>Works like <seealso marker="#lookup/2"><c>lookup/2</c></seealso>,
+ <p>Works like <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa>,
but does not return the objects. Returns <c>true</c> if one or more
table elements has key <c><anno>Key</anno></c>, otherwise
<c>false</c>.</p>
@@ -755,7 +755,7 @@ ok
<p>If an error occurs, the process is exited with an error
tuple <c>{error, Reason}</c>.</p>
<p>To find the first key in the table, use
- <seealso marker="#first/1"><c>first/1</c></seealso>.</p>
+ <seemfa marker="#first/1"><c>first/1</c></seemfa>.</p>
</desc>
</func>
@@ -787,16 +787,16 @@ ok
tuples, where the following values are allowed:</p>
<list type="bulleted">
<item>
- <p><c>{access, </c><seealso marker="#type-access">
- <c>access()</c></seealso><c>}</c> - Existing tables can be
+ <p><c>{access, </c><seetype marker="#access">
+ <c>access()</c></seetype><c>}</c> - Existing tables can be
opened in read-only mode. A table that is opened
in read-only mode is not subjected to the automatic file
reparation algorithm if it is later opened after a crash.
Defaults to <c>read_write</c>.</p>
</item>
<item>
- <p><c>{auto_save, </c><seealso marker="#type-auto_save">
- <c>auto_save()</c></seealso><c>}</c> - The autosave
+ <p><c>{auto_save, </c><seetype marker="#auto_save">
+ <c>auto_save()</c></seetype><c>}</c> - The autosave
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
@@ -806,18 +806,18 @@ ok
180000 (3 minutes).</p>
</item>
<item>
- <p><c>{estimated_no_objects, </c><seealso marker="#type-no_slots">
- <c>no_slots()</c></seealso><c>}</c> - Equivalent to option
+ <p><c>{estimated_no_objects, </c><seetype marker="#no_slots">
+ <c>no_slots()</c></seetype><c>}</c> - Equivalent to option
<c>min_no_slots</c>.</p>
</item>
<item>
- <p><c>{file, </c><seealso marker="file#type-name">
- <c>file:name()</c></seealso><c>}</c> - The name of the file to be
+ <p><c>{file, </c><seetype marker="file#name">
+ <c>file:name()</c></seetype><c>}</c> - The name of the file to be
opened. Defaults to the table name.</p>
</item>
<item>
- <p><c>{max_no_slots, </c><seealso marker="#type-no_slots">
- <c>no_slots()</c></seealso><c>}</c> - The maximum number
+ <p><c>{max_no_slots, </c><seetype marker="#no_slots">
+ <c>no_slots()</c></seetype><c>}</c> - The maximum number
of slots to be used. Defaults to 32 M, which is the
maximal value. Notice that a higher value can
increase the table fragmentation, and
@@ -825,16 +825,16 @@ ok
the expense of execution time.</p>
</item>
<item>
- <p><c>{min_no_slots, </c><seealso marker="#type-no_slots">
- <c>no_slots()</c></seealso><c>}</c> - Application
+ <p><c>{min_no_slots, </c><seetype marker="#no_slots">
+ <c>no_slots()</c></seetype><c>}</c> - Application
performance can be enhanced with this flag by specifying,
when the table is created, the estimated number of
different keys to be stored in the table. Defaults to 256,
which is the minimum value.</p>
</item>
<item>
- <p><c>{keypos, </c><seealso marker="#type-keypos">
- <c>keypos()</c></seealso><c>}</c> - The position of the
+ <p><c>{keypos, </c><seetype marker="#keypos">
+ <c>keypos()</c></seetype><c>}</c> - The position of the
element of each object to be used as key. Defaults to 1.
The ability to explicitly state the key
position is most convenient when we want to store Erlang
@@ -863,8 +863,8 @@ ok
<p>Option <c>repair</c> is ignored if the table is already open.</p>
</item>
<item>
- <p><c>{type, </c><seealso marker="#type-type">
- <c>type()</c></seealso><c>}</c> - The table type. Defaults to
+ <p><c>{type, </c><seetype marker="#type">
+ <c>type()</c></seetype><c>}</c> - The table type. Defaults to
<c>set</c>.</p>
</item>
</list>
@@ -889,8 +889,8 @@ ok
<desc>
<p>This function can be used to restore an opaque continuation
returned by
- <seealso marker="#select/3"><c>select/3</c></seealso> or
- <seealso marker="#select/1"><c>select/1</c></seealso> if the
+ <seemfa marker="#select/3"><c>select/3</c></seemfa> or
+ <seemfa marker="#select/1"><c>select/1</c></seemfa> if the
continuation has passed through external term format (been
sent between nodes or stored on disk).</p>
<p>The reason for this function is that continuation terms
@@ -901,7 +901,7 @@ ok
used in subsequent <c>select/1</c> calls even though it has
been stored on disk or on another node.</p>
<p>For more information and examples, see the
- <seealso marker="ets"><c>ets(3)</c></seealso> module.</p>
+ <seeerl marker="ets"><c>ets(3)</c></seeerl> module.</p>
<note>
<p>This function is rarely needed in application code. It is used by
application Mnesia to provide distributed <c>select/3</c>
@@ -933,7 +933,7 @@ ok
<c>next/2</c>, or select and match functions work as expected
even if the table is fixed; the limited support for
concurrency provided by the
- <seealso marker="ets"><c>ets(3)</c></seealso> module is not yet
+ <seeerl marker="ets"><c>ets(3)</c></seeerl> module is not yet
provided by Dets.
Fixing a table currently only disables resizing of
the hash list of the table.</p>
@@ -954,8 +954,8 @@ ok
table, the match specification, and the number of objects
that are matched are all defined by <c><anno>Continuation</anno></c>,
which is returned by a previous call to
- <seealso marker="#select/1"><c>select/1</c></seealso> or
- <seealso marker="#select/3"><c>select/3</c></seealso>.</p>
+ <seemfa marker="#select/1"><c>select/1</c></seemfa> or
+ <seemfa marker="#select/3"><c>select/3</c></seemfa>.</p>
<p>When all objects of the table have been matched,
<c>'$end_of_table'</c> is returned.</p>
</desc>
@@ -970,7 +970,7 @@ ok
<c><anno>MatchSpec</anno></c> to all or some objects stored in table
<c><anno>Name</anno></c>. The order of the objects is not specified.
For a description of match specifications, see the
- <seealso marker="erts:match_spec">ERTS User's Guide</seealso>.</p>
+ <seeguide marker="erts:match_spec">ERTS User's Guide</seeguide>.</p>
<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
@@ -992,12 +992,12 @@ ok
<c><anno>MatchSpec</anno></c> to some or all objects stored in table
<c><anno>Name</anno></c>. The order of the objects is not specified.
For a description of match specifications, see the
- <seealso marker="erts:match_spec">ERTS User's Guide</seealso>.</p>
+ <seeguide marker="erts:match_spec">ERTS User's Guide</seeguide>.</p>
<p>A tuple of the results of applying the match specification
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 more objects by calling
- <seealso marker="#select/1"><c>select/1</c></seealso>.</p>
+ <seemfa marker="#select/1"><c>select/1</c></seemfa>.</p>
<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 correct key(s). If the keypos'th element of
@@ -1012,7 +1012,7 @@ ok
match specification can be applied to more than <anno>N</anno>
objects.</p>
<p>The table is always to be protected using
- <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>
+ <seemfa marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seemfa>
before calling <c>select/3</c>, otherwise
errors can occur when calling <c>select/1</c>.</p>
</desc>
@@ -1027,7 +1027,7 @@ ok
applying match specification <c><anno>MatchSpec</anno></c> to the
object returns value <c>true</c>.
For a description of match specifications, see the
- <seealso marker="erts:match_spec">ERTS User's Guide</seealso>.
+ <seeguide marker="erts:match_spec">ERTS User's Guide</seeguide>.
Returns the number of deleted objects.</p>
<p>If the keypos'th element of <c><anno>MatchSpec</anno></c> is
bound, the match specification is applied to the objects
@@ -1070,16 +1070,16 @@ ok
<desc>
<p>Returns a Query List
Comprehension (QLC) query handle. The
- <seealso marker="qlc"><c>qlc(3)</c></seealso> module
+ <seeerl marker="qlc"><c>qlc(3)</c></seeerl> module
provides a query language aimed mainly for Mnesia, but
ETS tables, Dets tables, and lists are also recognized
by <c>qlc</c> as sources of data. Calling
- <seealso marker="dets#table/1"><c>dets:table/1,2</c></seealso> is the
+ <seemfa marker="dets#table/1"><c>dets:table/1,2</c></seemfa> is the
means to make 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
- <seealso marker="dets#lookup/2"><c>dets:lookup/2</c></seealso>
+ <seemfa marker="dets#lookup/2"><c>dets:lookup/2</c></seemfa>
to look up the keys. When
that is not possible, the whole table is traversed.
Option <c>traverse</c> determines how this is done:</p>
@@ -1090,8 +1090,8 @@ ok
</item>
<item>
<p><c>select</c> - The table is traversed by calling
- <seealso marker="dets#select/3"><c>dets:select/3</c></seealso> and
- <seealso marker="dets#select/1"><c>dets:select/1</c></seealso>.
+ <seemfa marker="dets#select/3"><c>dets:select/3</c></seemfa> and
+ <seemfa marker="dets#select/1"><c>dets:select/1</c></seemfa>.
Option <c>n_objects</c> determines the number of objects
returned (the third argument of <c>select/3</c>). The
match specification (the second argument of
@@ -1109,8 +1109,8 @@ ok
</list>
</item>
<item>
- <p><c>{select, </c><seealso marker="#type-match_spec">
- match_spec()</seealso><c>}</c> - As for <c>select</c>,
+ <p><c>{select, </c><seetype marker="#match_spec">
+ match_spec()</seetype><c>}</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 specified explicitly. This is how to
@@ -1213,9 +1213,9 @@ fun(X) -> {continue, X} end.</pre>
<section>
<title>See Also</title>
- <p><seealso marker="ets"><c>ets(3)</c></seealso>,
- <seealso marker="mnesia:mnesia"><c>mnesia(3)</c></seealso>,
- <seealso marker="qlc"><c>qlc(3)</c></seealso></p>
+ <p><seeerl marker="ets"><c>ets(3)</c></seeerl>,
+ <seeerl marker="mnesia:mnesia"><c>mnesia(3)</c></seeerl>,
+ <seeerl marker="qlc"><c>qlc(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/dict.xml b/lib/stdlib/doc/src/dict.xml
index 197269190e..aeb2584509 100644
--- a/lib/stdlib/doc/src/dict.xml
+++ b/lib/stdlib/doc/src/dict.xml
@@ -34,7 +34,7 @@
<p>This module provides a <c>Key</c>-<c>Value</c> dictionary.
The representation of a dictionary is not defined.</p>
<p>This module provides the same interface as the
- <seealso marker="orddict"><c>orddict(3)</c></seealso> module.
+ <seeerl marker="orddict"><c>orddict(3)</c></seeerl> module.
One difference is that while this module
considers two keys as different if they do not match (<c>=:=</c>),
<c>orddict</c> considers two keys as different if and only if
@@ -45,7 +45,7 @@
<datatype>
<name name="dict" n_vars="2"/>
<desc><p>Dictionary as returned by
- <seealso marker="#new/0"><c>new/0</c></seealso>.</p>
+ <seemfa marker="#new/0"><c>new/0</c></seemfa>.</p>
</desc>
</datatype>
<datatype>
@@ -60,7 +60,7 @@
<desc>
<p>Appends a new <c><anno>Value</anno></c> to the current list
of values associated with <c><anno>Key</anno></c>.</p>
- <p>See also section <seealso marker="#notes">Notes</seealso>.</p>
+ <p>See also section <seeerl marker="#notes">Notes</seeerl>.</p>
</desc>
</func>
@@ -72,7 +72,7 @@
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>
- <p>See also section <seealso marker="#notes">Notes</seealso>.</p>
+ <p>See also section <seeerl marker="#notes">Notes</seeerl>.</p>
</desc>
</func>
@@ -93,7 +93,7 @@
<c><anno>Key</anno></c> is present in dictionary <c>Dict</c>,
and an exception
is generated if <c><anno>Key</anno></c> is not in the dictionary.</p>
- <p>See also section <seealso marker="#notes">Notes</seealso>.</p>
+ <p>See also section <seeerl marker="#notes">Notes</seeerl>.</p>
</desc>
</func>
@@ -134,7 +134,7 @@
<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>
- <p>See also section <seealso marker="#notes">Notes</seealso>.</p>
+ <p>See also section <seeerl marker="#notes">Notes</seeerl>.</p>
</desc>
</func>
@@ -307,8 +307,8 @@ update_counter(Key, Incr, D) ->
<section>
<title>See Also</title>
- <p><seealso marker="gb_trees"><c>gb_trees(3)</c></seealso>,
- <seealso marker="orddict"><c>orddict(3)</c></seealso></p>
+ <p><seeerl marker="gb_trees"><c>gb_trees(3)</c></seeerl>,
+ <seeerl marker="orddict"><c>orddict(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml
index 51aabe4ef6..721429d819 100644
--- a/lib/stdlib/doc/src/digraph.xml
+++ b/lib/stdlib/doc/src/digraph.xml
@@ -39,13 +39,13 @@
directed graphs ("digraphs").</p>
<p>The digraphs managed by this module are stored in
- <seealso marker="ets">ETS tables</seealso>.
+ <seeerl marker="ets">ETS tables</seeerl>.
That implies the following:</p>
<list type="bulleted">
<item><p>Only the process that created the digraph is allowed to update it.</p></item>
<item><p>Digraphs will not be garbage collected. The ETS tables used for a
- digraph will only be deleted when <seealso marker="#delete/1"><c>delete/1</c></seealso>
+ digraph will only be deleted when <seemfa marker="#delete/1"><c>delete/1</c></seemfa>
is called or the process that created the digraph terminates.</p></item>
<item><p>A digraph is a mutable data structure.</p></item>
</list>
@@ -142,7 +142,7 @@
<datatype>
<name name="graph"/>
<desc><p>A digraph as returned by
- <seealso marker="#new/0"><c>new/0,1</c></seealso>.</p></desc>
+ <seemfa marker="#new/0"><c>new/0,1</c></seemfa>.</p></desc>
</datatype>
<datatype>
<name>edge()</name>
@@ -165,10 +165,10 @@
<desc>
<p><c>add_edge/5</c> creates (or modifies) edge <c><anno>E</anno></c>
of 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
+ the (new) <seeerl marker="#label">label</seeerl> of the edge. The
+ edge is <seeerl marker="#emanate">emanating</seeerl> from
<c><anno>V1</anno></c> and
- <seealso marker="#incident">incident</seealso>
+ <seeerl marker="#incident">incident</seeerl>
on <c><anno>V2</anno></c>. Returns <c><anno>E</anno></c>.</p>
<p><c>add_edge(<anno>G</anno>,&nbsp;<anno>V1</anno>,&nbsp;<anno>V2</anno>,&nbsp;<anno>Label</anno>)</c>
is equivalent to
@@ -181,7 +181,7 @@
<c>add_edge(<anno>G</anno>,&nbsp;<anno>V1</anno>,&nbsp;<anno>V2</anno>,&nbsp;[])</c>.
</p>
<p>If the edge would create a cycle in
- an <seealso marker="#acyclic_digraph">acyclic digraph</seealso>,
+ an <seeerl marker="#acyclic_digraph">acyclic digraph</seeerl>,
<c>{error,&nbsp;{bad_edge,&nbsp;<anno>Path</anno>}}</c> is returned.
If <c><anno>G</anno></c> already has an edge with value
<c><anno>E</anno></c> connecting a different pair of vertices,
@@ -204,7 +204,7 @@
<p><c>add_vertex/3</c> creates (or modifies) vertex
<c><anno>V</anno></c> of digraph <c><anno>G</anno></c>, using
<c><anno>Label</anno></c> as the (new)
- <seealso marker="#label">label</seealso> of the
+ <seeerl marker="#label">label</seeerl> of the
vertex. Returns <c><anno>V</anno></c>.</p>
<p><c>add_vertex(<anno>G</anno>,&nbsp;<anno>V</anno>)</c> is equivalent
to <c>add_vertex(<anno>G</anno>,&nbsp;<anno>V</anno>,&nbsp;[])</c>.
@@ -239,20 +239,20 @@
<fsummary>Delete paths from a digraph.</fsummary>
<desc>
<p>Deletes edges from digraph <c><anno>G</anno></c> until there are no
- <seealso marker="#path">paths</seealso> from vertex
+ <seeerl marker="#path">paths</seeerl> from vertex
<c><anno>V1</anno></c> to vertex <c><anno>V2</anno></c>.</p>
<p>A sketch of the procedure employed:</p>
<list type="bulleted">
<item>
<p>Find an arbitrary
- <seealso marker="#simple_path">simple path</seealso>
+ <seeerl marker="#simple_path">simple path</seeerl>
v[1],&nbsp;v[2],&nbsp;...,&nbsp;v[k] from <c><anno>V1</anno></c>
to <c><anno>V2</anno></c> in <c><anno>G</anno></c>.</p>
</item>
<item>
<p>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
+ <seeerl marker="#emanate">emanating</seeerl> from v[i] and
+ <seeerl marker="#incident">incident</seeerl> to v[i+1] for
1&nbsp;&lt;=&nbsp;i&nbsp;&lt;&nbsp;k (including multiple
edges).</p>
</item>
@@ -270,9 +270,9 @@
<desc>
<p>Deletes vertex <c><anno>V</anno></c> from digraph
<c><anno>G</anno></c>. Any edges
- <seealso marker="#emanate">emanating</seealso> from
+ <seeerl marker="#emanate">emanating</seeerl> from
<c><anno>V</anno></c> or
- <seealso marker="#incident">incident</seealso>
+ <seeerl marker="#incident">incident</seeerl>
on <c><anno>V</anno></c> are also deleted.</p>
</desc>
</func>
@@ -305,10 +305,10 @@
<p>Returns
<c>{<anno>E</anno>,&nbsp;<anno>V1</anno>,&nbsp;<anno>V2</anno>,&nbsp;<anno>Label</anno>}</c>,
where <c><anno>Label</anno></c> is the
- <seealso marker="#label">label</seealso> of edge
- <c><anno>E</anno></c> <seealso marker="#emanate">emanating</seealso>
+ <seeerl marker="#label">label</seeerl> of edge
+ <c><anno>E</anno></c> <seeerl marker="#emanate">emanating</seeerl>
from <c><anno>V1</anno></c> and
- <seealso marker="#incident">incident</seealso> on
+ <seeerl marker="#incident">incident</seeerl> on
<c><anno>V2</anno></c> of digraph <c><anno>G</anno></c>.
If no edge <c><anno>E</anno></c> of
digraph <c><anno>G</anno></c> exists, <c>false</c> is returned.</p>
@@ -330,8 +330,8 @@
a digraph.</fsummary>
<desc>
<p>Returns a list of all
- edges <seealso marker="#emanate">emanating</seealso> from or
- <seealso marker="#incident">incident</seealso> on<c><anno>V</anno></c>
+ edges <seeerl marker="#emanate">emanating</seeerl> from or
+ <seeerl marker="#incident">incident</seeerl> on<c><anno>V</anno></c>
of digraph <c><anno>G</anno></c>, in some unspecified order.</p>
</desc>
</func>
@@ -340,15 +340,15 @@
<name name="get_cycle" arity="2" since=""/>
<fsummary>Find one cycle in a digraph.</fsummary>
<desc>
- <p>If a <seealso marker="#simple_cycle">simple cycle</seealso> of
+ <p>If a <seeerl marker="#simple_cycle">simple cycle</seeerl> of
length two or more exists through vertex <c><anno>V</anno></c>, the
cycle is returned as a list
<c>[<anno>V</anno>,&nbsp;...,&nbsp;<anno>V</anno>]</c> of vertices.
- If a <seealso marker="#loop">loop</seealso> through
+ If a <seeerl marker="#loop">loop</seeerl> through
<c><anno>V</anno></c> exists, the loop is returned as a list
<c>[<anno>V</anno>]</c>. If no cycles through
<c><anno>V</anno></c> exist, <c>false</c> is returned.</p>
- <p><seealso marker="#get_path/3"><c>get_path/3</c></seealso> is used
+ <p><seemfa marker="#get_path/3"><c>get_path/3</c></seemfa> is used
for finding a simple cycle through <c><anno>V</anno></c>.</p>
</desc>
</func>
@@ -358,7 +358,7 @@
<fsummary>Find one path in a digraph.</fsummary>
<desc>
<p>Tries to find
- a <seealso marker="#simple_path">simple path</seealso> from vertex
+ a <seeerl marker="#simple_path">simple path</seeerl> from vertex
<c><anno>V1</anno></c> to vertex <c><anno>V2</anno></c> of digraph
<c><anno>G</anno></c>. Returns the path as a list
<c>[<anno>V1</anno>,&nbsp;...,&nbsp;<anno>V2</anno>]</c> of vertices,
@@ -374,15 +374,15 @@
<fsummary>Find one short cycle in a digraph.</fsummary>
<desc>
<p>Tries to find an as short as possible
- <seealso marker="#simple_cycle">simple cycle</seealso> through
+ <seeerl marker="#simple_cycle">simple cycle</seeerl> through
vertex <c><anno>V</anno></c> of digraph <c>G</c>. Returns the cycle
as a list <c>[<anno>V</anno>,&nbsp;...,&nbsp;<anno>V</anno>]</c>
of vertices, or
<c>false</c> if no simple cycle through <c><anno>V</anno></c> exists.
- Notice that a <seealso marker="#loop">loop</seealso> through
+ Notice that a <seeerl marker="#loop">loop</seeerl> through
<c><anno>V</anno></c> is returned as list
<c>[<anno>V</anno>,&nbsp;<anno>V</anno>]</c>.</p>
- <p><seealso marker="#get_short_path/3"><c>get_short_path/3</c></seealso>
+ <p><seemfa marker="#get_short_path/3"><c>get_short_path/3</c></seemfa>
is used for finding a simple cycle through <c><anno>V</anno></c>.</p>
</desc>
</func>
@@ -392,7 +392,7 @@
<fsummary>Find one short path in a digraph.</fsummary>
<desc>
<p>Tries to find an as short as possible
- <seealso marker="#simple_path">simple path</seealso> from vertex
+ <seeerl marker="#simple_path">simple path</seeerl> from vertex
<c><anno>V1</anno></c> to vertex <c><anno>V2</anno></c> of digraph
<c><anno>G</anno></c>. Returns the path as a list
<c>[<anno>V1</anno>,&nbsp;...,&nbsp;<anno>V2</anno>]</c> of
@@ -408,7 +408,7 @@
<name name="in_degree" arity="2" since=""/>
<fsummary>Return the in-degree of a vertex of a digraph.</fsummary>
<desc>
- <p>Returns the <seealso marker="#in_degree">in-degree</seealso> of
+ <p>Returns the <seeerl marker="#in_degree">in-degree</seeerl> of
vertex <c><anno>V</anno></c> of digraph <c><anno>G</anno></c>.</p>
</desc>
</func>
@@ -418,7 +418,7 @@
<fsummary>Return all edges incident on a vertex of a digraph.</fsummary>
<desc>
<p>Returns a list of all
- edges <seealso marker="#incident">incident</seealso> on
+ edges <seeerl marker="#incident">incident</seeerl> on
<c><anno>V</anno></c> of digraph <c><anno>G</anno></c>,
in some unspecified order.</p>
</desc>
@@ -429,7 +429,7 @@
<fsummary>Return all in-neighbors of a vertex of a digraph.</fsummary>
<desc>
<p>Returns a list of
- all <seealso marker="#in_neighbour">in-neighbors</seealso> of
+ all <seeerl marker="#in_neighbour">in-neighbors</seeerl> of
<c><anno>V</anno></c> of digraph <c><anno>G</anno></c>,
in some unspecified order.</p>
</desc>
@@ -483,15 +483,15 @@
<type name="d_protection"/>
<desc>
<p>Returns
- an <seealso marker="#empty_digraph">empty digraph</seealso> with
+ an <seeerl marker="#empty_digraph">empty digraph</seeerl> with
properties according to the options in <c><anno>Type</anno></c>:</p>
<taglist>
<tag><c>cyclic</c></tag>
- <item><p>Allows <seealso marker="#cycle">cycles</seealso> in the
+ <item><p>Allows <seeerl marker="#cycle">cycles</seeerl> in the
digraph (default).</p></item>
<tag><c>acyclic</c></tag>
<item><p>The digraph is to be kept
- <seealso marker="#acyclic_digraph">acyclic</seealso>.</p></item>
+ <seeerl marker="#acyclic_digraph">acyclic</seeerl>.</p></item>
<tag><c>protected</c></tag>
<item><p>Other processes can read the digraph (default).</p></item>
<tag><c>private</c></tag>
@@ -524,7 +524,7 @@
<name name="out_degree" arity="2" since=""/>
<fsummary>Return the out-degree of a vertex of a digraph.</fsummary>
<desc>
- <p>Returns the <seealso marker="#out_degree">out-degree</seealso> of
+ <p>Returns the <seeerl marker="#out_degree">out-degree</seeerl> of
vertex <c><anno>V</anno></c> of digraph <c><anno>G</anno></c>.</p>
</desc>
</func>
@@ -535,7 +535,7 @@
</fsummary>
<desc>
<p>Returns a list of all
- edges <seealso marker="#emanate">emanating</seealso> from
+ edges <seeerl marker="#emanate">emanating</seeerl> from
<c><anno>V</anno></c> of digraph <c><anno>G</anno></c>,
in some unspecified order.</p>
</desc>
@@ -546,7 +546,7 @@
<fsummary>Return all out-neighbors of a vertex of a digraph.</fsummary>
<desc>
<p>Returns a list of
- all <seealso marker="#out_neighbour">out-neighbors</seealso> of
+ all <seeerl marker="#out_neighbour">out-neighbors</seeerl> of
<c><anno>V</anno></c> of digraph <c><anno>G</anno></c>,
in some unspecified order.</p>
</desc>
@@ -558,7 +558,7 @@
<desc>
<p>Returns <c>{<anno>V</anno>,&nbsp;<anno>Label</anno>}</c>,
where <c><anno>Label</anno></c> is the
- <seealso marker="#label">label</seealso> of the vertex
+ <seeerl marker="#label">label</seeerl> of the vertex
<c><anno>V</anno></c> of digraph <c><anno>G</anno></c>,
or <c>false</c> if no vertex <c><anno>V</anno></c>
of digraph <c><anno>G</anno></c> exists.</p>
@@ -577,8 +577,8 @@
<section>
<title>See Also</title>
- <p><seealso marker="digraph_utils"><c>digraph_utils(3)</c></seealso>,
- <seealso marker="ets"><c>ets(3)</c></seealso></p>
+ <p><seeerl marker="digraph_utils"><c>digraph_utils(3)</c></seeerl>,
+ <seeerl marker="ets"><c>ets(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/digraph_utils.xml b/lib/stdlib/doc/src/digraph_utils.xml
index 8739445852..94fcc26ee5 100644
--- a/lib/stdlib/doc/src/digraph_utils.xml
+++ b/lib/stdlib/doc/src/digraph_utils.xml
@@ -37,7 +37,7 @@
<description>
<p>This module provides algorithms based on depth-first traversal of
directed graphs. For basic functions on directed graphs, see the
- <seealso marker="digraph"><c>digraph(3)</c></seealso> module.</p>
+ <seeerl marker="digraph"><c>digraph(3)</c></seeerl> module.</p>
<list type="bulleted">
<item>
@@ -158,7 +158,7 @@
<fsummary>Check if a digraph is an arborescence.</fsummary>
<desc>
<p>Returns <c>{yes, <anno>Root</anno>}</c> if <c><anno>Root</anno></c>
- is the <seealso marker="#root">root</seealso> of the arborescence
+ is the <seeerl marker="#root">root</seeerl> of the arborescence
<c><anno>Digraph</anno></c>, otherwise <c>no</c>.</p>
</desc>
</func>
@@ -168,7 +168,7 @@
<fsummary>Return the components of a digraph.</fsummary>
<desc>
<p>Returns a list
- of <seealso marker="#components">connected components.</seealso>.
+ of <seeerl marker="#components">connected components.</seeerl>.
Each component is represented by its
vertices. The order of the vertices and the order of the
components are arbitrary. Each vertex of digraph
@@ -181,22 +181,22 @@
<fsummary>Return a condensed graph of a digraph.</fsummary>
<desc>
<p>Creates a digraph where the vertices are
- the <seealso marker="#strong_components">strongly connected
- components</seealso> of <c><anno>Digraph</anno></c> as returned by
- <seealso marker="#strong_components/1">
- <c>strong_components/1</c></seealso>.
+ the <seeerl marker="#strong_components">strongly connected
+ components</seeerl> of <c><anno>Digraph</anno></c> as returned by
+ <seemfa marker="#strong_components/1">
+ <c>strong_components/1</c></seemfa>.
If X and Y are two different strongly
connected components, and vertices x and y exist in X
and Y, respectively, such that there is an
- edge <seealso marker="#emanate">emanating</seealso> from x
- and <seealso marker="#incident">incident</seealso> on y, then
+ edge <seeerl marker="#emanate">emanating</seeerl> from x
+ and <seeerl marker="#incident">incident</seeerl> 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><anno>Digraph</anno></c>.
All vertices and edges have the
- default <seealso marker="#label">label</seealso> <c>[]</c>.</p>
- <p>Each <seealso marker="#cycle">cycle</seealso> is
+ default <seeerl marker="#label">label</seeerl> <c>[]</c>.</p>
+ <p>Each <seeerl marker="#cycle">cycle</seeerl> is
included in some strongly connected component, which implies that
- a <seealso marker="#topsort">topological ordering</seealso> of the
+ a <seeerl marker="#topsort">topological ordering</seeerl> of the
created digraph always exists.</p>
</desc>
</func>
@@ -205,15 +205,15 @@
<name name="cyclic_strong_components" arity="1" since=""/>
<fsummary>Return the cyclic strong components of a digraph.</fsummary>
<desc>
- <p>Returns a list of <seealso marker="#strong_components">strongly
- connected components</seealso>. Each strongly component is represented
+ <p>Returns a list of <seeerl marker="#strong_components">strongly
+ connected components</seeerl>. Each strongly component is represented
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
+ included in some <seeerl marker="#cycle">cycle</seeerl> in
<c><anno>Digraph</anno></c> are returned, otherwise the returned
list is equal to that returned by
- <seealso marker="#strong_components/1">
- <c>strong_components/1</c></seealso>.</p>
+ <seemfa marker="#strong_components/1">
+ <c>strong_components/1</c></seemfa>.</p>
</desc>
</func>
@@ -223,7 +223,7 @@
<desc>
<p>Returns <c>true</c> if and only if digraph
<c><anno>Digraph</anno></c> is
- <seealso marker="#acyclic_digraph">acyclic</seealso>.</p>
+ <seeerl marker="#acyclic_digraph">acyclic</seeerl>.</p>
</desc>
</func>
@@ -233,7 +233,7 @@
<desc>
<p>Returns <c>true</c> if and only if digraph
<c><anno>Digraph</anno></c> is
- an <seealso marker="#arborescence">arborescence</seealso>.</p>
+ an <seeerl marker="#arborescence">arborescence</seeerl>.</p>
</desc>
</func>
@@ -243,7 +243,7 @@
<desc>
<p>Returns <c>true</c> if and only if digraph
<c><anno>Digraph</anno></c> is
- a <seealso marker="#tree">tree</seealso>.</p>
+ a <seeerl marker="#tree">tree</seeerl>.</p>
</desc>
</func>
@@ -253,7 +253,7 @@
</fsummary>
<desc>
<p>Returns a list of all vertices of <c><anno>Digraph</anno></c> that
- are included in some <seealso marker="#loop">loop</seealso>.</p>
+ are included in some <seeerl marker="#loop">loop</seeerl>.</p>
</desc>
</func>
@@ -263,8 +263,8 @@
<desc>
<p>Returns all vertices of 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
+ a <seeerl marker="#depth_first_traversal">depth-first
+ traversal</seeerl> of the digraph, collecting visited
vertices in postorder. More precisely, the vertices visited
while searching from an arbitrarily chosen vertex are
collected in postorder, and all those collected vertices are
@@ -278,8 +278,8 @@
<desc>
<p>Returns all vertices of 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
+ a <seeerl marker="#depth_first_traversal">depth-first
+ traversal</seeerl> of the digraph, collecting visited
vertices in preorder.</p>
</desc>
</func>
@@ -291,7 +291,7 @@
<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><anno>Digraph</anno></c>
+ <seeerl marker="#path">path</seeerl> in <c><anno>Digraph</anno></c>
from some
vertex of <c><anno>Vertices</anno></c> to the vertex. In particular,
as paths can have length zero, the vertices of
@@ -306,12 +306,12 @@
<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><anno>Digraph</anno></c>
+ <seeerl marker="#path">path</seeerl> 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><anno>Vertices</anno></c> that are included in
- some <seealso marker="#cycle">cycle</seealso> are returned.</p>
+ some <seeerl marker="#cycle">cycle</seeerl> are returned.</p>
</desc>
</func>
@@ -322,7 +322,7 @@
<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
+ a <seeerl marker="#path">path</seeerl> from the vertex to some
vertex of <c><anno>Vertices</anno></c>. In particular, as paths
can have length zero, the vertices of <c><anno>Vertices</anno></c>
are included in the returned list.</p>
@@ -336,11 +336,11 @@
<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
+ a <seeerl marker="#path">path</seeerl> of length one or more
from the vertex to some vertex of <c><anno>Vertices</anno></c>.
Therefore only those vertices of <c><anno>Vertices</anno></c>
that are included
- in some <seealso marker="#cycle">cycle</seealso> are returned.</p>
+ in some <seeerl marker="#cycle">cycle</seeerl> are returned.</p>
</desc>
</func>
@@ -348,8 +348,8 @@
<name name="strong_components" arity="1" since=""/>
<fsummary>Return the strong components of a digraph.</fsummary>
<desc>
- <p>Returns a list of <seealso marker="#strong_components">strongly
- connected components</seealso>.
+ <p>Returns a list of <seeerl marker="#strong_components">strongly
+ connected components</seeerl>.
Each strongly component is represented
by its vertices. The order of the vertices and the order of
the components are arbitrary. Each vertex of digraph
@@ -363,7 +363,7 @@
<name name="subgraph" arity="3" since=""/>
<fsummary>Return a subgraph of a digraph.</fsummary>
<desc>
- <p>Creates a maximal <seealso marker="#subgraph">subgraph</seealso>
+ <p>Creates a maximal <seeerl marker="#subgraph">subgraph</seeerl>
of <c>Digraph</c> having
as vertices those vertices of <c><anno>Digraph</anno></c> that are
mentioned in <c><anno>Vertices</anno></c>.</p>
@@ -371,10 +371,10 @@
the default, 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
- <seealso marker="digraph#new/1"><c>digraph:new/1</c></seealso>.</p>
+ <seemfa marker="digraph#new/1"><c>digraph:new/1</c></seemfa>.</p>
<p>If the value of option <c>keep_labels</c> is <c>true</c>,
which is the default,
- the <seealso marker="#label">labels</seealso> of vertices and edges
+ the <seeerl marker="#label">labels</seeerl> of vertices and edges
of <c><anno>Digraph</anno></c> are used for the subgraph as well. If
the value is <c>false</c>, default label <c>[]</c> is used
for the vertices and edges of the subgroup.</p>
@@ -391,11 +391,11 @@
<fsummary>Return a topological sorting of the vertices of a digraph.
</fsummary>
<desc>
- <p>Returns a <seealso marker="#topsort">topological
- ordering</seealso> of the vertices of digraph
+ <p>Returns a <seeerl marker="#topsort">topological
+ ordering</seeerl> of the vertices of digraph
<c><anno>Digraph</anno></c> if such an ordering exists, otherwise
<c>false</c>. For each vertex in the returned list,
- no <seealso marker="#out_neighbour">out-neighbors</seealso>
+ no <seeerl marker="#out_neighbour">out-neighbors</seeerl>
occur earlier in the list.</p>
</desc>
</func>
@@ -403,7 +403,7 @@
<section>
<title>See Also</title>
- <p><seealso marker="digraph"><c>digraph(3)</c></seealso></p>
+ <p><seeerl marker="digraph"><c>digraph(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml
index 2244b02908..29d3a488c8 100644
--- a/lib/stdlib/doc/src/epp.xml
+++ b/lib/stdlib/doc/src/epp.xml
@@ -36,7 +36,7 @@
<modulesummary>An Erlang code preprocessor.</modulesummary>
<description>
<p>The Erlang code preprocessor includes functions that are used by the
- <seealso marker="compiler:compile"><c>compile</c></seealso>
+ <seeerl marker="compiler:compile"><c>compile</c></seeerl>
module to preprocess macros and include files before
the parsing takes place.</p>
@@ -98,10 +98,10 @@
<desc>
<p>Returns a string representation of an encoding. The string
is recognized by
- <seealso marker="#read_encoding/1"><c>read_encoding/1,2</c></seealso>,
- <seealso marker="#read_encoding_from_binary/1">
- <c>read_encoding_from_binary/1,2</c></seealso>, and
- <seealso marker="#set_encoding/1"><c>set_encoding/1,2</c></seealso>
+ <seemfa marker="#read_encoding/1"><c>read_encoding/1,2</c></seemfa>,
+ <seemfa marker="#read_encoding_from_binary/1">
+ <c>read_encoding_from_binary/1,2</c></seemfa>, and
+ <seemfa marker="#set_encoding/1"><c>set_encoding/1,2</c></seemfa>
as a valid encoding.</p>
</desc>
</func>
@@ -115,7 +115,7 @@
describes the error or warning. This function is usually
called implicitly when processing an <c>ErrorInfo</c>
structure (see section
- <seealso marker="#errorinfo">Error Information</seealso>).</p>
+ <seeerl marker="#errorinfo">Error Information</seeerl>).</p>
</desc>
</func>
@@ -198,7 +198,7 @@
<name name="read_encoding" arity="2" since="OTP R16B"/>
<fsummary>Read the encoding from a file.</fsummary>
<desc>
- <p>Read the <seealso marker="#encoding">encoding</seealso> from
+ <p>Read the <seeerl marker="#encoding">encoding</seeerl> from
a file. Returns the read encoding, or <c>none</c> if no
valid encoding is found.</p>
<p>Option <c>in_comment_only</c> is <c>true</c> by
@@ -213,7 +213,7 @@
<name name="read_encoding_from_binary" arity="2" since="OTP R16B"/>
<fsummary>Read the encoding from a binary.</fsummary>
<desc>
- <p>Read the <seealso marker="#encoding">encoding</seealso> from
+ <p>Read the <seeerl marker="#encoding">encoding</seeerl> from
a binary. Returns the read encoding, or <c>none</c> if no
valid encoding is found.</p>
<p>Option <c>in_comment_only</c> is <c>true</c> by
@@ -227,7 +227,7 @@
<name name="set_encoding" arity="1" since="OTP R16B"/>
<fsummary>Read and set the encoding of an I/O device.</fsummary>
<desc>
- <p>Reads the <seealso marker="#encoding">encoding</seealso> from
+ <p>Reads the <seeerl marker="#encoding">encoding</seeerl> from
an I/O device and sets the encoding of the device
accordingly. The position of the I/O device referenced by
<c><anno>File</anno></c> is not affected. If no valid
@@ -242,13 +242,13 @@
<name name="set_encoding" arity="2" since="OTP 17.0"/>
<fsummary>Read and set the encoding of an I/O device.</fsummary>
<desc>
- <p>Reads the <seealso marker="#encoding">encoding</seealso> from
+ <p>Reads the <seeerl marker="#encoding">encoding</seeerl> from
an I/O device and sets the encoding of the device
accordingly. The position of the I/O device referenced by
<c><anno>File</anno></c> is not affected. If no valid
encoding can be read from the I/O device, the encoding of the
I/O device is set to the
- <seealso marker="#encoding">encoding</seealso> specified by
+ <seeerl marker="#encoding">encoding</seeerl> specified by
<c><anno>Default</anno></c>.</p>
<p>Returns the read encoding, or <c>none</c> if no valid
encoding is found.</p>
@@ -270,7 +270,7 @@ Module:format_error(ErrorDescriptor)</code>
<section>
<title>See Also</title>
- <p><seealso marker="erl_parse"><c>erl_parse(3)</c></seealso></p>
+ <p><seeerl marker="erl_parse"><c>erl_parse(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_anno.xml b/lib/stdlib/doc/src/erl_anno.xml
index 8026aa827b..6c3f0eef4b 100644
--- a/lib/stdlib/doc/src/erl_anno.xml
+++ b/lib/stdlib/doc/src/erl_anno.xml
@@ -78,30 +78,30 @@
<tag><c>record</c></tag>
<item><p>A Boolean indicating if the origin of the abstract
code is a record. Used by
- <seealso marker="dialyzer:dialyzer">Dialyzer</seealso>
+ <seeerl marker="dialyzer:dialyzer">Dialyzer</seeerl>
to assign types to tuple elements.</p>
</item>
</taglist>
<p>The functions
- <seealso marker="erl_scan#column/1"><c>column()</c></seealso>,
- <seealso marker="erl_scan#end_location/1"><c>end_location()</c></seealso>,
- <seealso marker="erl_scan#line/1"><c>line()</c></seealso>,
- <seealso marker="erl_scan#location/1"><c>location()</c></seealso>, and
- <seealso marker="erl_scan#text/1"><c>text()</c></seealso>
+ <seemfa marker="erl_scan#column/1"><c>column()</c></seemfa>,
+ <seemfa marker="erl_scan#end_location/1"><c>end_location()</c></seemfa>,
+ <seemfa marker="erl_scan#line/1"><c>line()</c></seemfa>,
+ <seemfa marker="erl_scan#location/1"><c>location()</c></seemfa>, and
+ <seemfa marker="erl_scan#text/1"><c>text()</c></seemfa>
in the <c>erl_scan</c> module can be used for inspecting
annotations in tokens.</p>
<p>The functions
- <seealso marker="erl_parse#anno_from_term/1">
- <c>anno_from_term()</c></seealso>,
- <seealso marker="erl_parse#anno_to_term/1">
- <c>anno_to_term()</c></seealso>,
- <seealso marker="erl_parse#fold_anno/3"><c>fold_anno()</c></seealso>,
- <seealso marker="erl_parse#map_anno/2"><c>map_anno()</c></seealso>,
- <seealso marker="erl_parse#mapfold_anno/3">
- <c>mapfold_anno()</c></seealso>,
- and <seealso marker="erl_parse#new_anno/1"><c>new_anno()</c></seealso>,
+ <seemfa marker="erl_parse#anno_from_term/1">
+ <c>anno_from_term()</c></seemfa>,
+ <seemfa marker="erl_parse#anno_to_term/1">
+ <c>anno_to_term()</c></seemfa>,
+ <seemfa marker="erl_parse#fold_anno/3"><c>fold_anno()</c></seemfa>,
+ <seemfa marker="erl_parse#map_anno/2"><c>map_anno()</c></seemfa>,
+ <seemfa marker="erl_parse#mapfold_anno/3">
+ <c>mapfold_anno()</c></seemfa>,
+ and <seemfa marker="erl_parse#new_anno/1"><c>new_anno()</c></seemfa>,
in the <c>erl_parse</c> module can be
used for manipulating annotations in abstract code.</p>
</description>
@@ -169,7 +169,7 @@
<fsummary>Return annotations given a term.</fsummary>
<desc>
<p>Returns annotations with representation <anno>Term</anno>.</p>
- <p>See also <seealso marker="#to_term/1">to_term()</seealso>.</p>
+ <p>See also <seemfa marker="#to_term/1">to_term()</seemfa>.</p>
</desc>
</func>
@@ -291,14 +291,14 @@
</fsummary>
<desc>
<p>Returns the term representing the annotations <anno>Anno</anno>.</p>
- <p>See also <seealso marker="#from_term/1">from_term()</seealso>.</p>
+ <p>See also <seemfa marker="#from_term/1">from_term()</seemfa>.</p>
</desc>
</func>
</funcs>
<section>
<title>See Also</title>
- <p><seealso marker="erl_parse"><c>erl_parse(3)</c></seealso>,
- <seealso marker="erl_scan"><c>erl_scan(3)</c></seealso></p>
+ <p><seeerl marker="erl_parse"><c>erl_parse(3)</c></seeerl>,
+ <seeerl marker="erl_scan"><c>erl_scan(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_eval.xml b/lib/stdlib/doc/src/erl_eval.xml
index a35b1b34dd..e54dcdbdc9 100644
--- a/lib/stdlib/doc/src/erl_eval.xml
+++ b/lib/stdlib/doc/src/erl_eval.xml
@@ -37,8 +37,8 @@
<description>
<p>This module provides an interpreter for Erlang expressions. The
expressions are in the abstract syntax as returned by
- <seealso marker="erl_parse"><c>erl_parse</c></seealso>,
- the Erlang parser, or <seealso marker="io"><c>io</c></seealso>.</p>
+ <seeerl marker="erl_parse"><c>erl_parse</c></seeerl>,
+ the Erlang parser, or <seeerl marker="io"><c>io</c></seeerl>.</p>
</description>
<datatypes>
@@ -54,10 +54,10 @@
</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>
+ <desc><p>As returned by <seemfa marker="erl_parse#parse_exprs/1">
+ <c>erl_parse:parse_exprs/1</c></seemfa> or
+ <seemfa marker="io#parse_erl_exprs/2">
+ <c>io:parse_erl_exprs/2</c></seemfa>.</p></desc>
</datatype>
<datatype>
<name name="expression_list"/>
@@ -74,8 +74,8 @@
<datatype>
<name name="local_function_handler"/>
<desc><p>Further described in section
- <seealso marker="#local_function_handler">
- Local Function Handler</seealso> in this module</p></desc>
+ <seeerl marker="#local_function_handler">
+ Local Function Handler</seeerl> in this module</p></desc>
</datatype>
<datatype>
<name name="name"/>
@@ -86,8 +86,8 @@
<datatype>
<name name="non_local_function_handler"/>
<desc><p>Further described in section
- <seealso marker="#non_local_function_handler">
- Non-Local Function Handler</seealso> in this module.</p></desc>
+ <seeerl marker="#non_local_function_handler">
+ Non-Local Function Handler</seeerl> in this module.</p></desc>
</datatype>
<datatype>
<name name="value"/>
@@ -146,10 +146,10 @@
For an explanation of when and how to use arguments
<c><anno>LocalFunctionHandler</anno></c> and
<c><anno>NonLocalFunctionHandler</anno></c>, see sections
- <seealso marker="#local_function_handler">
- Local Function Handler</seealso> and
- <seealso marker="#non_local_function_handler">
- Non-Local Function Handler</seealso> in this module.</p>
+ <seeerl marker="#local_function_handler">
+ Local Function Handler</seeerl> and
+ <seeerl marker="#non_local_function_handler">
+ Non-Local Function Handler</seeerl> in this module.</p>
<p>Returns <c>{value, <anno>Value</anno>, <anno>NewBindings</anno>}</c>
by default. If <c><anno>ReturnFormat</anno></c> is <c>value</c>,
only <c><anno>Value</anno></c> is returned.</p>
@@ -166,8 +166,8 @@
initial bindings for each expression. Attempts are made to
merge the bindings returned from each evaluation. This
function is useful in <c>LocalFunctionHandler</c>, see section
- <seealso marker="#local_function_handler">
- Local Function Handler</seealso> in this module.</p>
+ <seeerl marker="#local_function_handler">
+ Local Function Handler</seeerl> in this module.</p>
<p>Returns <c>{<anno>ValueList</anno>, <anno>NewBindings</anno>}</c>.
</p>
</desc>
@@ -182,15 +182,15 @@
<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 that can
- be returned by <seealso marker="io#parse_erl_exprs/2">
- <c>io:parse_erl_exprs/2</c></seealso>.
+ be returned by <seemfa marker="io#parse_erl_exprs/2">
+ <c>io:parse_erl_exprs/2</c></seemfa>.
For an explanation of when and how to use arguments
<c><anno>LocalFunctionHandler</anno></c> and
<c><anno>NonLocalFunctionHandler</anno></c>, see sections
- <seealso marker="#local_function_handler">
- Local Function Handler</seealso> and
- <seealso marker="#non_local_function_handler">
- Non-Local Function Handler</seealso> in this module.</p>
+ <seeerl marker="#local_function_handler">
+ Local Function Handler</seeerl> and
+ <seeerl marker="#non_local_function_handler">
+ Non-Local Function Handler</seeerl> in this module.</p>
<p>Returns <c>{value, <anno>Value</anno>, <anno>NewBindings</anno>}</c>
</p>
</desc>
diff --git a/lib/stdlib/doc/src/erl_expand_records.xml b/lib/stdlib/doc/src/erl_expand_records.xml
index 0980af94b1..a7f273158c 100644
--- a/lib/stdlib/doc/src/erl_expand_records.xml
+++ b/lib/stdlib/doc/src/erl_expand_records.xml
@@ -55,7 +55,7 @@
<section>
<title>See Also</title>
- <p>Section <seealso marker="erts:absform">The Abstract Format</seealso>
+ <p>Section <seeguide marker="erts:absform">The Abstract Format</seeguide>
in ERTS User's Guide.</p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_id_trans.xml b/lib/stdlib/doc/src/erl_id_trans.xml
index 686f3148a4..337cb6215f 100644
--- a/lib/stdlib/doc/src/erl_id_trans.xml
+++ b/lib/stdlib/doc/src/erl_id_trans.xml
@@ -49,9 +49,9 @@
<name since="">parse_transform(Forms, Options) -> Forms</name>
<fsummary>Transform Erlang forms.</fsummary>
<type>
- <v>Forms = [<seealso marker="erl_parse#type-abstract_form">erl_parse:abstract_form()</seealso>
- | <seealso marker="erl_parse#type-form_info">erl_parse:form_info()</seealso>]</v>
- <v>Options = [<seealso marker="compile#type-option">compile:option()</seealso>]</v>
+ <v>Forms = [<seetype marker="erl_parse#abstract_form">erl_parse:abstract_form()</seetype>
+ | <seetype marker="erl_parse#form_info">erl_parse:form_info()</seetype>]</v>
+ <v>Options = [<seetype marker="compile#option">compile:option()</seetype>]</v>
</type>
<desc>
<p>Performs an identity transformation on Erlang forms, as an example.
@@ -73,8 +73,8 @@
<section>
<title>See Also</title>
- <p><seealso marker="erl_parse"><c>erl_parse(3)</c></seealso>,
- <seealso marker="compiler:compile"><c>compile(3)</c></seealso></p>
+ <p><seeerl marker="erl_parse"><c>erl_parse(3)</c></seeerl>,
+ <seeerl marker="compiler:compile"><c>compile(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_lint.xml b/lib/stdlib/doc/src/erl_lint.xml
index 6c8102c37a..bd33a00a48 100644
--- a/lib/stdlib/doc/src/erl_lint.xml
+++ b/lib/stdlib/doc/src/erl_lint.xml
@@ -85,7 +85,7 @@
that describes the error or warning. This function is usually
called implicitly when processing an <c>ErrorInfo</c> structure
(see section
- <seealso marker="#errorinfo">Error Information</seealso>).</p>
+ <seeerl marker="#errorinfo">Error Information</seeerl>).</p>
</desc>
</func>
@@ -95,8 +95,8 @@
<desc>
<p>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. <seealso marker="erl_parse#parse_exprs/1">
- <c>erl_parse:parse_exprs(Tokens)</c></seealso>
+ form for the expression. <seemfa marker="erl_parse#parse_exprs/1">
+ <c>erl_parse:parse_exprs(Tokens)</c></seemfa>
can be used to generate a list of <c><anno>Expr</anno></c>.</p>
</desc>
</func>
@@ -122,14 +122,14 @@
compiler, and to avoid the same description in two places, the
elements of <c>Options</c> that control the warnings are
only described in the
- <seealso marker="compiler:compile#erl_lint_options">
- <c>compile(3)</c></seealso> module.</p>
+ <seeerl marker="compiler:compile#erl_lint_options">
+ <c>compile(3)</c></seeerl> module.</p>
<p><c><anno>AbsForms</anno></c> of a module, which comes from a file
that is read through <c>epp</c>, the Erlang preprocessor, can come
from many files. This means that any references to errors must
- include the filename, see the <seealso marker="epp">
- <c>epp(3)</c></seealso> module or parser (see the
- <seealso marker="erl_parse"><c>erl_parse(3)</c></seealso> module).
+ include the filename, see the <seeerl marker="epp">
+ <c>epp(3)</c></seeerl> module or parser (see the
+ <seeerl marker="erl_parse"><c>erl_parse(3)</c></seeerl> module).
The returned errors and warnings have the following format:</p>
<code type="none">
[{<anno>FileName2</anno>,[<anno>ErrorInfo</anno>]}]</code>
@@ -154,8 +154,8 @@ Module:format_error(ErrorDescriptor)</code>
<section>
<title>See Also</title>
- <p><seealso marker="epp"><c>epp(3)</c></seealso>,
- <seealso marker="erl_parse"><c>erl_parse(3)</c></seealso></p>
+ <p><seeerl marker="epp"><c>epp(3)</c></seeerl>,
+ <seeerl marker="erl_parse"><c>erl_parse(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml
index 9bbf3adb49..25f5d4d905 100644
--- a/lib/stdlib/doc/src/erl_parse.xml
+++ b/lib/stdlib/doc/src/erl_parse.xml
@@ -38,10 +38,10 @@
<p>This module is the basic Erlang parser that converts tokens into
the abstract form of either forms (that is, top-level constructs),
expressions, or terms. The Abstract Format is described in the
- <seealso marker="erts:absform">ERTS User's Guide</seealso>.
+ <seeguide marker="erts:absform">ERTS User's Guide</seeguide>.
Notice that a token list must end with the <em>dot</em> token to be
- acceptable to the parse functions (see the <seealso marker="erl_scan">
- <c>erl_scan(3)</c></seealso>) module.</p>
+ acceptable to the parse functions (see the <seeerl marker="erl_scan">
+ <c>erl_scan(3)</c></seeerl>) module.</p>
</description>
<datatypes>
@@ -69,6 +69,31 @@
<name name="erl_parse_tree"></name>
</datatype>
<datatype>
+ <name>af_binelement(_)</name>
+ <desc>
+ <p>Abstract representation of an element of a bitstring.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name>af_field_decl()</name>
+ <desc>
+ <p>Abstract representation of a record field.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name>af_generator()</name>
+ <desc>
+ <p>Abstract representation of a generator
+ or a bitstring generator.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name>af_remote_function()></name>
+ <desc>
+ <p>Abstract representation of a remote function call.</p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="error_description"></name>
</datatype>
<datatype>
@@ -95,7 +120,7 @@
<p>Converts the Erlang data structure <c><anno>Data</anno></c> into an
abstract form of type <c><anno>AbsTerm</anno></c>.
This function is the inverse of
- <seealso marker="#normalise/1"><c>normalise/1</c></seealso>.</p>
+ <seemfa marker="#normalise/1"><c>normalise/1</c></seemfa>.</p>
<p><c>erl_parse:abstract(T)</c> is equivalent to
<c>erl_parse:abstract(T, 0)</c>.</p>
</desc>
@@ -113,8 +138,8 @@
<p>Option <c><anno>Encoding</anno></c> is used for
selecting which integer lists to be considered
as strings. The default is to use the encoding returned by
- function <seealso marker="epp#default_encoding/0">
- <c>epp:default_encoding/0</c></seealso>.
+ function <seemfa marker="epp#default_encoding/0">
+ <c>epp:default_encoding/0</c></seemfa>.
Value <c>none</c> means that no integer lists are
considered as strings. <c>encoding_func()</c> is
called with one integer of a list at a time; if it
@@ -132,8 +157,8 @@
say <c>T</c>, where a <c>erl_parse</c> tree has collections
of annotations. Returns a <c>erl_parse</c> tree where each
term <c>T</c> is replaced by the value returned by
- <seealso marker="erl_anno#from_term/1">
- <c>erl_anno:from_term(T)</c></seealso>. The term
+ <seemfa marker="erl_anno#from_term/1">
+ <c>erl_anno:from_term(T)</c></seemfa>. The term
<c><anno>Term</anno></c> is traversed in a depth-first,
left-to-right fashion.</p>
</desc>
@@ -146,8 +171,8 @@
<p>Returns a term where each collection of annotations
<c>Anno</c> of the nodes of the <c>erl_parse</c> tree
<c><anno>Abstr</anno></c> is replaced by the term
- returned by <seealso marker="erl_anno#to_term/1">
- <c>erl_anno:to_term(Anno)</c></seealso>. The
+ returned by <seemfa marker="erl_anno#to_term/1">
+ <c>erl_anno:to_term(Anno)</c></seemfa>. The
<c>erl_parse</c> tree is traversed in a depth-first,
left-to-right fashion.</p>
</desc>
@@ -174,16 +199,16 @@
<name since="">format_error(ErrorDescriptor) -> Chars</name>
<fsummary>Format an error descriptor.</fsummary>
<type>
- <v>ErrorDescriptor = <seealso
- marker="#type-error_info">error_description()</seealso></v>
+ <v>ErrorDescriptor = <seetype
+ marker="#error_info">error_description()</seetype></v>
<v>Chars = [char() | Chars]</v>
</type>
<desc>
<p>Uses an <c>ErrorDescriptor</c> and returns a string
that describes the error. This function is usually called
implicitly when an <c>ErrorInfo</c> structure is processed
- (see section <seealso marker="#errorinfo">
- Error Information</seealso>).</p>
+ (see section <seeerl marker="#errorinfo">
+ Error Information</seeerl>).</p>
</desc>
</func>
@@ -224,12 +249,12 @@
<fsummary>Create new annotations.</fsummary>
<desc>
<p>Assumes that <c><anno>Term</anno></c> is a term with the same
- structure as a <c>erl_parse</c> tree, but with <seealso
- marker="erl_anno#type-location">locations</seealso> where a
+ structure as a <c>erl_parse</c> tree, but with <seetype
+ marker="erl_anno#location">locations</seetype> where a
<c>erl_parse</c> tree has collections of annotations.
Returns a <c>erl_parse</c> tree where each location <c>L</c>
- is replaced by the value returned by <seealso
- marker="erl_anno#new/1"><c>erl_anno:new(L)</c></seealso>.
+ is replaced by the value returned by <seemfa
+ marker="erl_anno#new/1"><c>erl_anno:new(L)</c></seemfa>.
The term <c><anno>Term</anno></c> is traversed in a
depth-first, left-to-right fashion.</p>
</desc>
@@ -242,7 +267,7 @@
<p>Converts the abstract form <c><anno>AbsTerm</anno></c> of a
term into a conventional Erlang data structure (that is, the
term itself). This function is the inverse of
- <seealso marker="#abstract/1"><c>abstract/1</c></seealso>.</p>
+ <seemfa marker="#abstract/1"><c>abstract/1</c></seemfa>.</p>
</desc>
</func>
@@ -332,10 +357,10 @@ Module:format_error(ErrorDescriptor)</code>
<section>
<title>See Also</title>
- <p><seealso marker="erl_anno"><c>erl_anno(3)</c></seealso>,
- <seealso marker="erl_scan"><c>erl_scan(3)</c></seealso>,
- <seealso marker="io"><c>io(3)</c></seealso>,
- section <seealso marker="erts:absform">The Abstract Format</seealso>
+ <p><seeerl marker="erl_anno"><c>erl_anno(3)</c></seeerl>,
+ <seeerl marker="erl_scan"><c>erl_scan(3)</c></seeerl>,
+ <seeerl marker="io"><c>io(3)</c></seeerl>,
+ section <seeguide marker="erts:absform">The Abstract Format</seeguide>
in the ERTS User's Guide</p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml
index 0a46139db6..0600881fbe 100644
--- a/lib/stdlib/doc/src/erl_pp.xml
+++ b/lib/stdlib/doc/src/erl_pp.xml
@@ -57,7 +57,7 @@
is to be a valid expression. If <c>HookFunction</c> is equal to
<c>none</c>, there is no hook function.</p>
<p>The called hook function is to return a (possibly deep) list of
- characters. Function <seealso marker="#expr/4"><c>expr/4</c></seealso>
+ characters. Function <seemfa marker="#expr/4"><c>expr/4</c></seemfa>
is useful in a hook.</p>
<p>If <c><anno>CurrentIndentation</anno></c> is negative, there are no
line breaks and only a space is used as a separator.</p>
@@ -68,6 +68,10 @@
<desc>
<p>The option <c>quote_singleton_atom_types</c>
is used to add quotes to all singleton atom types.</p>
+ <p>The option <c>linewidth</c> controls the maximum line
+ width for formatted lines (defaults to 72 characters).</p>
+ <p>The option <c>indent</c> controls the
+ indention for formatted lines (defaults to 4 spaces).</p>
</desc>
</datatype>
<datatype>
@@ -81,7 +85,7 @@
<name name="attribute" arity="2" since=""/>
<fsummary>Pretty print an attribute.</fsummary>
<desc>
- <p>Same as <seealso marker="#form/1"><c>form/1,2</c></seealso>,
+ <p>Same as <seemfa marker="#form/1"><c>form/1,2</c></seemfa>,
but only for attribute <c><anno>Attribute</anno></c>.</p>
</desc>
</func>
@@ -95,7 +99,7 @@
<desc>
<p>Prints one expression. It is useful for implementing hooks (see
section
- <seealso marker="#knownlimitations">Known Limitations</seealso>).</p>
+ <seeerl marker="#knownlimitations">Known Limitations</seeerl>).</p>
</desc>
</func>
@@ -105,7 +109,7 @@
<name name="exprs" arity="3" since=""/>
<fsummary>Pretty print <c>Expressions</c>.</fsummary>
<desc>
- <p>Same as <seealso marker="#form/1"><c>form/1,2</c></seealso>,
+ <p>Same as <seemfa marker="#form/1"><c>form/1,2</c></seemfa>,
but only for the sequence of
expressions in <c><anno>Expressions</anno></c>.</p>
</desc>
@@ -118,8 +122,8 @@
<desc>
<p>Pretty prints a
<c><anno>Form</anno></c>, which is an abstract form of a type that is
- returned by <seealso marker="erl_parse#parse_form/1">
- <c>erl_parse:parse_form/1</c></seealso>.</p>
+ returned by <seemfa marker="erl_parse#parse_form/1">
+ <c>erl_parse:parse_form/1</c></seemfa>.</p>
</desc>
</func>
@@ -128,7 +132,7 @@
<name name="function" arity="2" since=""/>
<fsummary>Pretty print a function.</fsummary>
<desc>
- <p>Same as <seealso marker="#form/1"><c>form/1,2</c></seealso>,
+ <p>Same as <seemfa marker="#form/1"><c>form/1,2</c></seemfa>,
but only for function <c><anno>Function</anno></c>.</p>
</desc>
</func>
@@ -138,7 +142,7 @@
<name name="guard" arity="2" since=""/>
<fsummary>Pretty print a guard.</fsummary>
<desc>
- <p>Same as <seealso marker="#form/1"><c>form/1,2</c></seealso>,
+ <p>Same as <seemfa marker="#form/1"><c>form/1,2</c></seemfa>,
but only for the guard test <c><anno>Guard</anno></c>.</p>
</desc>
</func>
@@ -153,9 +157,9 @@
<section>
<title>See Also</title>
- <p><seealso marker="erl_eval"><c>erl_eval(3)</c></seealso>,
- <seealso marker="erl_parse"><c>erl_parse(3)</c></seealso>,
- <seealso marker="io"><c>io(3)</c></seealso></p>
+ <p><seeerl marker="erl_eval"><c>erl_eval(3)</c></seeerl>,
+ <seeerl marker="erl_parse"><c>erl_parse(3)</c></seeerl>,
+ <seeerl marker="io"><c>io(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_scan.xml b/lib/stdlib/doc/src/erl_scan.xml
index 92c4d5627c..4cfad284e7 100644
--- a/lib/stdlib/doc/src/erl_scan.xml
+++ b/lib/stdlib/doc/src/erl_scan.xml
@@ -108,7 +108,7 @@
that describes the error or warning. This function is usually
called implicitly when an <c>ErrorInfo</c> structure is
processed (see section
- <seealso marker="#errorinfo">Error Information</seealso>).</p>
+ <seeerl marker="#errorinfo">Error Information</seeerl>).</p>
</desc>
</func>
@@ -180,10 +180,10 @@
line where the token begins, as well as the text of the
token (if option <c>text</c> is specified), all of which can
be accessed by calling
- <seealso marker="#column/1"><c>column/1</c></seealso>,
- <seealso marker="#line/1"><c>line/1</c></seealso>,
- <seealso marker="#location/1"><c>location/1</c></seealso>, and
- <seealso marker="#text/1"><c>text/1</c></seealso>.</p>
+ <seemfa marker="#column/1"><c>column/1</c></seemfa>,
+ <seemfa marker="#line/1"><c>line/1</c></seemfa>,
+ <seemfa marker="#location/1"><c>location/1</c></seemfa>, and
+ <seemfa marker="#text/1"><c>text/1</c></seemfa>.</p>
<p>A <em>token</em> is a tuple containing information about
syntactic category, the token annotations, and the
terminal symbol. For punctuation characters (such as <c>;</c> and
@@ -296,7 +296,7 @@
<c>tokens(<anno>Continuation</anno>, <anno>CharSpec</anno>,
<anno>StartLocation</anno>, [])</c>.</p>
<p>For a description of the options, see
- <seealso marker="#string/3"><c>string/3</c></seealso>.</p>
+ <seemfa marker="#string/3"><c>string/3</c></seemfa>.</p>
</desc>
</func>
</funcs>
@@ -323,8 +323,8 @@ Module:format_error(ErrorDescriptor)</code>
<section>
<title>See Also</title>
- <p><seealso marker="erl_anno"><c>erl_anno(3)</c></seealso>,
- <seealso marker="erl_parse"><c>erl_parse(3)</c></seealso>,
- <seealso marker="io"><c>io(3)</c></seealso></p>
+ <p><seeerl marker="erl_anno"><c>erl_anno(3)</c></seeerl>,
+ <seeerl marker="erl_parse"><c>erl_parse(3)</c></seeerl>,
+ <seeerl marker="io"><c>io(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_tar.xml b/lib/stdlib/doc/src/erl_tar.xml
index be55dc0777..ace73372c2 100644
--- a/lib/stdlib/doc/src/erl_tar.xml
+++ b/lib/stdlib/doc/src/erl_tar.xml
@@ -48,41 +48,41 @@
To abide to the convention, add "<c>.tar</c>" to the name.</p>
<p>Tar files can be created in one operation using function
- <seealso marker="#create/2"><c>create/2</c></seealso> or
- <seealso marker="#create/3"><c>create/3</c></seealso>.</p>
+ <seemfa marker="#create/2"><c>create/2</c></seemfa> or
+ <seemfa marker="#create/3"><c>create/3</c></seemfa>.</p>
<p>Alternatively, for more control, use functions
- <seealso marker="#open/2"><c>open/2</c></seealso>,
- <seealso marker="#add/3"><c>add/3,4</c></seealso>, and
- <seealso marker="#close/1"><c>close/1</c></seealso>.</p>
+ <seemfa marker="#open/2"><c>open/2</c></seemfa>,
+ <seemfa marker="#add/3"><c>add/3,4</c></seemfa>, and
+ <seemfa marker="#close/1"><c>close/1</c></seemfa>.</p>
<p>To extract all files from a tar file, use function
- <seealso marker="#extract/1"><c>extract/1</c></seealso>.
+ <seemfa marker="#extract/1"><c>extract/1</c></seemfa>.
To extract only some files or to be able to specify some more options,
- use function <seealso marker="#extract/2"><c>extract/2</c></seealso>.</p>
+ use function <seemfa marker="#extract/2"><c>extract/2</c></seemfa>.</p>
<p>To return a list of the files in a tar file,
- use function <seealso marker="#table/1"><c>table/1</c></seealso> or
- <seealso marker="#table/2"><c>table/2</c></seealso>.
+ use function <seemfa marker="#table/1"><c>table/1</c></seemfa> or
+ <seemfa marker="#table/2"><c>table/2</c></seemfa>.
To print a list of files to the Erlang shell,
- use function <seealso marker="#t/1"><c>t/1</c></seealso> or
- <seealso marker="#tt/1"><c>tt/1</c></seealso>.</p>
+ use function <seemfa marker="#t/1"><c>t/1</c></seemfa> or
+ <seemfa marker="#tt/1"><c>tt/1</c></seemfa>.</p>
<p>To convert an error term returned from one of the functions
above to a readable message, use function
- <seealso marker="#format_error/1"><c>format_error/1</c></seealso>.</p>
+ <seemfa marker="#format_error/1"><c>format_error/1</c></seemfa>.</p>
</description>
<section>
<title>Unicode Support</title>
- <p>If <seealso marker="kernel:file#native_name_encoding/0">
- <c>file:native_name_encoding/0</c></seealso>
+ <p>If <seemfa marker="kernel:file#native_name_encoding/0">
+ <c>file:native_name_encoding/0</c></seemfa>
returns <c>utf8</c>, path names are encoded in UTF-8 when
creating tar files, and path names are assumed to be encoded in
UTF-8 when extracting tar files.</p>
- <p>If <seealso marker="kernel:file#native_name_encoding/0">
- <c>file:native_name_encoding/0</c></seealso>
+ <p>If <seemfa marker="kernel:file#native_name_encoding/0">
+ <c>file:native_name_encoding/0</c></seemfa>
returns <c>latin1</c>, no translation of path names is done.</p>
<p>Unicode metadata stored in PAX headers is preserved</p>
@@ -90,16 +90,16 @@
<section>
<title>Other Storage Media</title>
- <p>The <seealso marker="ftp:ftp"><c>ftp</c></seealso>
+ <p>The <seeerl marker="ftp:ftp"><c>ftp</c></seeerl>
module normally accesses the tar file on disk using
- the <seealso marker="kernel:file"><c>file</c></seealso> module.
+ the <seeerl marker="kernel:file"><c>file</c></seeerl> module.
When other needs arise, you can define your own low-level Erlang
functions to perform the writing and reading on the storage media;
- use function <seealso marker="#init/3"><c>init/3</c></seealso>.</p>
+ use function <seemfa marker="#init/3"><c>init/3</c></seemfa>.</p>
<p>An example of this is the SFTP support in
- <seealso marker="ssh:ssh_sftp#open_tar/3">
- <c>ssh_sftp:open_tar/3</c></seealso>. This function opens a tar file
+ <seemfa marker="ssh:ssh_sftp#open_tar/3">
+ <c>ssh_sftp:open_tar/3</c></seemfa>. This function opens a tar file
on a remote machine using an SFTP channel.</p>
</section>
@@ -146,7 +146,7 @@
<type name="add_opt"/>
<desc>
<p>Adds a file to a tar file that has been opened for writing by
- <seealso marker="#open/2"><c>open/1</c></seealso>.</p>
+ <seemfa marker="#open/2"><c>open/1</c></seemfa>.</p>
<p><c>NameInArchive</c> is the name under which the file becomes
stored in the tar file. The file gets this name when it is
extracted from the tar file.</p>
@@ -168,44 +168,44 @@
<p>Reads data in parts from the file. This is intended for
memory-limited machines that, for example, builds a tar file
on a remote machine over SFTP, see
- <seealso marker="ssh:ssh_sftp#open_tar/3">
- <c>ssh_sftp:open_tar/3</c></seealso>.</p>
+ <seemfa marker="ssh:ssh_sftp#open_tar/3">
+ <c>ssh_sftp:open_tar/3</c></seemfa>.</p>
</item>
<tag><c>{atime,non_neg_integer()}</c></tag>
<item>
<p>Sets the last time, as
- <seealso marker="erts:time_correction#POSIX_Time">
- POSIX time</seealso>, when the file was read. See also
- <seealso marker="kernel:file#read_file_info/1">
- <c>file:read_file_info/1</c></seealso>.</p>
+ <seeguide marker="erts:time_correction#POSIX_Time">
+ POSIX time</seeguide>, when the file was read. See also
+ <seemfa marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seemfa>.</p>
</item>
<tag><c>{mtime,non_neg_integer()}</c></tag>
<item>
<p>Sets the last time, as
- <seealso marker="erts:time_correction#POSIX_Time">
- POSIX time</seealso>, when the file was written. See also
- <seealso marker="kernel:file#read_file_info/1">
- <c>file:read_file_info/1</c></seealso>.</p>
+ <seeguide marker="erts:time_correction#POSIX_Time">
+ POSIX time</seeguide>, when the file was written. See also
+ <seemfa marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seemfa>.</p>
</item>
<tag><c>{ctime,non_neg_integer()}</c></tag>
<item>
<p>Sets the time, as
- <seealso marker="erts:time_correction#POSIX_Time">
- POSIX time</seealso>, when the file was created. See also
- <seealso marker="kernel:file#read_file_info/1">
- <c>file:read_file_info/1</c></seealso>.</p>
+ <seeguide marker="erts:time_correction#POSIX_Time">
+ POSIX time</seeguide>, when the file was created. See also
+ <seemfa marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seemfa>.</p>
</item>
<tag><c>{uid,non_neg_integer()}</c></tag>
<item>
<p>Sets the file owner.
- <seealso marker="kernel:file#read_file_info/1">
- <c>file:read_file_info/1</c></seealso>.</p>
+ <seemfa marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seemfa>.</p>
</item>
<tag><c>{gid,non_neg_integer()}</c></tag>
<item>
<p>Sets the group that the file owner belongs to.
- <seealso marker="kernel:file#read_file_info/1">
- <c>file:read_file_info/1</c></seealso>.</p>
+ <seemfa marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seemfa>.</p>
</item>
</taglist>
</desc>
@@ -216,7 +216,7 @@
<fsummary>Close an open tar file.</fsummary>
<desc>
<p>Closes a tar file
- opened by <seealso marker="#open/2"><c>open/2</c></seealso>.</p>
+ opened by <seemfa marker="#open/2"><c>open/2</c></seemfa>.</p>
</desc>
</func>
@@ -288,6 +288,11 @@
writing the file. That is, absolute paths will be turned into
relative paths. There will be an info message written to the error
logger when paths are changed in this way.</p></note>
+ <warning>
+ <p>The <c>compressed</c> and <c>cooked</c> flags are invalid when
+ passing a file descriptor with <c>{file,Fd}</c>. The file is
+ assumed to have been opened with the appropriate flags.</p>
+ </warning>
</desc>
</func>
@@ -349,6 +354,11 @@
<p>Prints an informational message for each extracted file.</p>
</item>
</taglist>
+ <warning>
+ <p>The <c>compressed</c> and <c>cooked</c> flags are invalid when
+ passing a file descriptor with <c>{file,Fd}</c>. The file is
+ assumed to have been opened with the appropriate flags.</p>
+ </warning>
</desc>
</func>
@@ -401,14 +411,14 @@
<tag><c>(position,{UserData,Position})</c></tag>
<item>
<p>Sets the position of <c>UserData</c> as defined for files in
- <seealso marker="kernel:file#position-2">
- <c>file:position/2</c></seealso></p>
+ <seemfa marker="kernel:file#position/2">
+ <c>file:position/2</c></seemfa></p>
</item>
</taglist>
<p><em>Example:</em></p>
<p>The following is a complete <c>Fun</c> parameter for reading and
writing on files using the
- <seealso marker="kernel:file"><c>file</c></seealso> module:</p>
+ <seeerl marker="kernel:file"><c>file</c></seeerl> module:</p>
<code type="none">
ExampleFun =
fun(write, {Fd,Data}) -> file:write(Fd, Data);
@@ -431,7 +441,7 @@ erl_tar:close(TarDesc)</code>
<note>
<p>This example with the <c>file</c> module operations is
not necessary to use directly, as that is what function
- <seealso marker="#open/2"><c>open/2</c></seealso> in principle
+ <seemfa marker="#open/2"><c>open/2</c></seemfa> in principle
does.</p>
</note>
<warning>
@@ -470,14 +480,19 @@ erl_tar:close(TarDesc)</code>
</item>
</taglist>
<p>To add one file at the time into an opened tar file, use function
- <seealso marker="#add/3"><c>add/3,4</c></seealso>. When you are
- finished adding files, use function <seealso marker="#close/1">
- <c>close/1</c></seealso> to close the tar file.</p>
+ <seemfa marker="#add/3"><c>add/3,4</c></seemfa>. When you are
+ finished adding files, use function <seemfa marker="#close/1">
+ <c>close/1</c></seemfa> to close the tar file.</p>
+ <warning>
+ <p>The <c>compressed</c> and <c>cooked</c> flags are invalid when
+ passing a file descriptor with <c>{file,Fd}</c>. The file must
+ already be opened with the appropriate flags.</p>
+ </warning>
<warning>
<p>The <c>TarDescriptor</c> term is not a file descriptor. You are
advised not to rely on the specific contents of this term, as it
can change in future Erlang/OTP releases when more features are
- added to this module..</p>
+ added to this module.</p>
</warning>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index f7e18fbbf8..4f43ae28d0 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -57,10 +57,10 @@
The previous default limit was about 1400 tables and
could be increased by setting the environment variable
<c>ERL_MAX_ETS_TABLES</c> or the command line option
- <seealso marker="erts:erl#+e"><c>+e</c></seealso> before starting the
+ <seecom marker="erts:erl#+e"><c>+e</c></seecom> before starting the
Erlang runtime system. This hard limit has been removed, but it is currently
useful to set the <c>ERL_MAX_ETS_TABLES</c> anyway. It should be
- set to an approximate of the maximum amount of tables used. This since
+ set to an approximate of the maximum amount of tables used since
an internal table for named tables is sized using this value. If
large amounts of named tables are used and <c>ERL_MAX_ETS_TABLES</c>
hasn't been increased, the performance of named table lookup will
@@ -72,11 +72,11 @@
Even if there are no references to a table from any process, it
is not automatically destroyed unless the owner process
terminates. To destroy a table explicitly, use function
- <seealso marker="#delete/1"><c>delete/1</c></seealso>.
+ <seemfa marker="#delete/1"><c>delete/1</c></seemfa>.
The default owner is the process that created the
table. To transfer table ownership at process termination, use
- option <seealso marker="#heir"><c>heir</c></seealso> or call
- <seealso marker="#give_away/3"><c>give_away/3</c></seealso>.</p>
+ option <seeerl marker="#heir"><c>heir</c></seeerl> or call
+ <seemfa marker="#give_away/3"><c>give_away/3</c></seemfa>.</p>
<p>Some implementation details:</p>
@@ -85,8 +85,8 @@
look-up operation results in a copy of the object.</p></item>
<item><p><c>'$end_of_table'</c> is not to be used as a key, as
this atom is used to mark the end of the table when using functions
- <seealso marker="#first/1"><c>first/1</c></seealso> and
- <seealso marker="#next/2"><c>next/2</c></seealso>.</p></item>
+ <seemfa marker="#first/1"><c>first/1</c></seemfa> and
+ <seemfa marker="#next/2"><c>next/2</c></seemfa>.</p></item>
</list>
<p>Notice the subtle difference between
@@ -116,13 +116,41 @@
</list>
</description>
- <section>
- <title>Failure</title>
- <p>The functions in this module exits with reason
- <c>badarg</c> if any argument has the wrong format, if the
- table identifier is invalid, or if the operation is denied because of
- table access rights (<seealso marker="#protected">protected</seealso>
- or <seealso marker="#private">private</seealso>).</p>
+ <section><marker id="ets_failures"></marker>
+ <title>Failures</title>
+ <p>Functions in this module fail by raising an error exception
+ with error reason:</p>
+ <taglist>
+ <tag><c>badarg</c></tag>
+ <item><p>
+ If any argument has the wrong format.
+ </p></item>
+ <tag><c>badarg</c></tag>
+ <item><p>
+ If the table identifier is invalid.
+ </p></item>
+ <tag><c>badarg</c></tag>
+ <item><p>
+ If the operation is denied because of
+ table access rights (<seeerl marker="#protected">protected</seeerl>
+ or <seeerl marker="#private">private</seeerl>).
+ </p></item>
+ <tag><c>system_limit</c></tag>
+ <item><p>
+ Modification of a value causes it to not be representable
+ internally in the VM. For example, incrementation of a
+ counter past the largest integer representable.
+ </p></item>
+ <tag><c>system_limit</c></tag>
+ <item><p>
+ If a match specification passed as argument has excessive
+ nesting which causes scheduler stack exhaustion for the
+ scheduler that the calling process is executing on.
+ <seecom marker="erts:erl#sched_thread_stack_size">Scheduler
+ stack size</seecom> can be configured when starting the
+ runtime system.
+ </p></item>
+ </taglist>
</section>
<section><marker id="concurrency"></marker>
@@ -145,32 +173,42 @@
<p>There are different ways to traverse through the objects of a table.</p>
<list type="bulleted">
<item><p><em>Single-step</em> traversal one key at at time, using
- <seealso marker="#first/1"><c>first/1</c></seealso>,
- <seealso marker="#next/2"><c>next/2</c></seealso>,
- <seealso marker="#last/1"><c>last/1</c></seealso> and
- <seealso marker="#prev/2"><c>prev/2</c></seealso>.</p>
+ <seemfa marker="#first/1"><c>first/1</c></seemfa>,
+ <seemfa marker="#next/2"><c>next/2</c></seemfa>,
+ <seemfa marker="#last/1"><c>last/1</c></seemfa> and
+ <seemfa marker="#prev/2"><c>prev/2</c></seemfa>.</p>
</item>
<item><p>Search with simple <em>match patterns</em>, using
- <seealso marker="#match/1"><c>match/1/2/3</c></seealso>,
- <seealso marker="#match_delete/2"><c>match_delete/2</c></seealso> and
- <seealso marker="#match_object/1"><c>match_object/1/2/3</c></seealso>.</p>
+ <seemfa marker="#match/1"><c>match/1/2/3</c></seemfa>,
+ <seemfa marker="#match_delete/2"><c>match_delete/2</c></seemfa> and
+ <seemfa marker="#match_object/1"><c>match_object/1/2/3</c></seemfa>.</p>
</item>
<item><p>Search with more powerful <em>match specifications</em>, using
- <seealso marker="#select/1"><c>select/1/2/3</c></seealso>,
- <seealso marker="#select_count/2"><c>select_count/2</c></seealso>,
- <seealso marker="#select_delete/2"><c>select_delete/2</c></seealso>,
- <seealso marker="#select_replace/2"><c>select_replace/2</c></seealso> and
- <seealso marker="#select_reverse/1"><c>select_reverse/1/2/3</c></seealso>.</p>
+ <seemfa marker="#select/1"><c>select/1/2/3</c></seemfa>,
+ <seemfa marker="#select_count/2"><c>select_count/2</c></seemfa>,
+ <seemfa marker="#select_delete/2"><c>select_delete/2</c></seemfa>,
+ <seemfa marker="#select_replace/2"><c>select_replace/2</c></seemfa> and
+ <seemfa marker="#select_reverse/1"><c>select_reverse/1/2/3</c></seemfa>.</p>
</item>
<item><p><em>Table conversions</em>, using
- <seealso marker="#tab2file/2"><c>tab2file/2/3</c></seealso> and
- <seealso marker="#tab2list/1"><c>tab2list/1</c></seealso>.</p>
+ <seemfa marker="#tab2file/2"><c>tab2file/2/3</c></seemfa> and
+ <seemfa marker="#tab2list/1"><c>tab2list/1</c></seemfa>.</p>
</item>
</list>
- <p>None of these ways of table traversal will guarantee a consistent table snapshot
- if the table is also updated during the traversal. Moreover, traversals not
- done in a <em>safe</em> way, on tables where keys are inserted or deleted
- during the traversal, may yield the following undesired effects:</p>
+ <p>
+ No table traversal will guarantee a consistent snapshot of the entire
+ table if the table is also updated by concurrent processes during the
+ traversal. The result of each concurrently updated object may be seen (or
+ not) depending on if it has happened when the traversal visits that part
+ of the table. The only way to guarantee a full consistent table snapshot
+ (if you really need that) is to disallow concurrent updates during the
+ entire traversal.
+ </p>
+ <p>
+ Moreover, traversals not done in a <em>safe</em> way, on tables where
+ keys are inserted or deleted during the traversal, may yield the
+ following undesired effects:
+ </p>
<list type="bulleted">
<item><p>Any key may be missed.</p></item>
<item><p>Any key may be found more than once.</p></item>
@@ -184,13 +222,13 @@
<item><p>the entire table traversal is done within one ETS function
call.</p>
</item>
- <item><p>function <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>
+ <item><p>function <seemfa marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seemfa>
is used to keep the table fixated during the entire traversal.</p>
</item>
</list>
<note>
<p>Even though the access of a single object is always guaranteed to be
- <seealso marker="#concurrency">atomic and isolated</seealso>, each traversal
+ <seeerl marker="#concurrency">atomic and isolated</seeerl>, each traversal
through a table to find the next key is not done with such guarantees. This is often
not a problem, but may cause rare subtle "unexpected" effects if a concurrent
process inserts objects during a traversal. For example, consider one
@@ -209,7 +247,7 @@ ets:insert(t, {3}),
ordered directly after <c>1</c>.</p>
<p>Effects like this are improbable but possible. The probability will
further be reduced (if not vanish) if table option
- <seealso marker="#new_2_write_concurrency"><c>write_concurrency</c></seealso>
+ <seeerl marker="#new_2_write_concurrency"><c>write_concurrency</c></seeerl>
is not enabled. This can also only be a potential concern for
<c>ordered_set</c> where the traversal order is defined.</p>
</note>
@@ -235,9 +273,12 @@ true
<title>Match Specifications</title>
<p>Some of the functions use a <em>match specification</em>,
<c>match_spec</c>. For a brief explanation, see
- <seealso marker="#select/2"><c>select/2</c></seealso>. For a detailed
- description, see section <seealso marker="erts:match_spec">
- Match Specifications in Erlang</seealso> in ERTS User's Guide.</p>
+ <seemfa marker="#select/2"><c>select/2</c></seemfa>. For a detailed
+ description, see section <seeguide marker="erts:match_spec">
+ Match Specifications in Erlang</seeguide> in ERTS User's Guide.</p>
+ <p>A match specifications with excessive nesting will cause a
+ <seeerl marker="#ets_failures"><c>system_limit</c></seeerl> error
+ exception to be raised.</p>
</section>
<datatypes>
@@ -247,11 +288,11 @@ true
<datatype>
<name>continuation()</name>
<desc>
- <p>Opaque continuation used by <seealso marker="#select/1">
- <c>select/1,3</c></seealso>, <seealso marker="#select_reverse/1">
- <c>select_reverse/1,3</c></seealso>, <seealso marker="#match/1">
- <c>match/1,3</c></seealso>, and <seealso marker="#match_object/1">
- <c>match_object/1,3</c></seealso>.</p>
+ <p>Opaque continuation used by <seemfa marker="#select/1">
+ <c>select/1,3</c></seemfa>, <seemfa marker="#select_reverse/1">
+ <c>select_reverse/1,3</c></seemfa>, <seemfa marker="#match/1">
+ <c>match/1,3</c></seemfa>, and <seemfa marker="#match_object/1">
+ <c>match_object/1,3</c></seemfa>.</p>
</desc>
</datatype>
<datatype>
@@ -271,7 +312,7 @@ true
<datatype>
<name name="tid"/>
<desc><p>A table identifier, as returned by
- <seealso marker="#new/2"><c>new/2</c></seealso>.</p></desc>
+ <seemfa marker="#new/2"><c>new/2</c></seemfa>.</p></desc>
</datatype>
<datatype>
<name name="type"/>
@@ -318,7 +359,7 @@ true
<desc>
<p>Delete all objects in the ETS table <c><anno>Tab</anno></c>.
The operation is guaranteed to be
- <seealso marker="#concurrency">atomic and isolated</seealso>.</p>
+ <seeerl marker="#concurrency">atomic and isolated</seeerl>.</p>
</desc>
</func>
@@ -338,9 +379,9 @@ true
<name name="file2tab" arity="1" since=""/>
<fsummary>Read an ETS table from a file.</fsummary>
<desc>
- <p>Reads a file produced by <seealso marker="#tab2file/2">
- <c>tab2file/2</c></seealso> or
- <seealso marker="#tab2file/3"><c>tab2file/3</c></seealso> and
+ <p>Reads a file produced by <seemfa marker="#tab2file/2">
+ <c>tab2file/2</c></seemfa> or
+ <seemfa marker="#tab2file/3"><c>tab2file/3</c></seemfa> and
creates the corresponding table <c><anno>Tab</anno></c>.</p>
<p>Equivalent to <c>file2tab(<anno>Filename</anno>, [])</c>.</p>
</desc>
@@ -350,16 +391,16 @@ true
<name name="file2tab" arity="2" since=""/>
<fsummary>Read an ETS table from a file.</fsummary>
<desc>
- <p>Reads a file produced by <seealso marker="#tab2file/2">
- <c>tab2file/2</c></seealso> or <seealso marker="#tab2file/3">
- <c>tab2file/3</c></seealso> and creates the
+ <p>Reads a file produced by <seemfa marker="#tab2file/2">
+ <c>tab2file/2</c></seemfa> or <seemfa marker="#tab2file/3">
+ <c>tab2file/3</c></seemfa> and creates the
corresponding table <c><anno>Tab</anno></c>.</p>
<p>The only supported option is <c>{verify,boolean()}</c>.
If verification is turned on (by specifying <c>{verify,true}</c>),
the function uses whatever information is present in the file to
assert that the information is not damaged. How this is done depends
on which <c>extended_info</c> was written using
- <seealso marker="#tab2file/3"><c>tab2file/3</c></seealso>.</p>
+ <seemfa marker="#tab2file/3"><c>tab2file/3</c></seemfa>.</p>
<p>If no <c>extended_info</c> is present in the file and
<c>{verify,true}</c> is specified, the number of objects
written is compared to the size of the original table when the
@@ -368,7 +409,7 @@ true
table was dumped to file. To avoid this problem,
either do not verify files dumped while updated simultaneously
or use option <c>{extended_info, [object_count]}</c> to
- <seealso marker="#tab2file/3"><c>tab2file/3</c></seealso>, which
+ <seemfa marker="#tab2file/3"><c>tab2file/3</c></seemfa>, which
extends the information in the file with the number of objects
written.</p>
<p>If verification is turned on and the file was written with
@@ -389,7 +430,7 @@ true
order of the table is returned. If the table is empty,
<c>'$end_of_table'</c> is returned.</p>
<p>To find subsequent keys in the table, use
- <seealso marker="#next/2"><c>next/2</c></seealso>.</p>
+ <seemfa marker="#next/2"><c>next/2</c></seemfa>.</p>
</desc>
</func>
@@ -399,7 +440,7 @@ true
<desc>
<p><c><anno>Acc0</anno></c> is returned if the table is empty.
This function is similar to
- <seealso marker="lists#foldl/3"><c>lists:foldl/3</c></seealso>.
+ <seemfa marker="lists#foldl/3"><c>lists:foldl/3</c></seemfa>.
The table elements are traversed in an unspecified order, except for
<c>ordered_set</c> tables, where they are traversed first to last.</p>
<p>If <c><anno>Function</anno></c> inserts objects into the table,
@@ -415,7 +456,7 @@ true
<desc>
<p><c><anno>Acc0</anno></c> is returned if the table is empty.
This function is similar to
- <seealso marker="lists#foldr/3"><c>lists:foldr/3</c></seealso>.
+ <seemfa marker="lists#foldr/3"><c>lists:foldr/3</c></seemfa>.
The table elements are traversed in an unspecified order, except for
<c>ordered_set</c> tables, where they are traversed last to first.</p>
<p>If <c><anno>Function</anno></c> inserts objects into the table,
@@ -447,7 +488,7 @@ true
<p>Pseudo function that by a <c>parse_transform</c> translates
<c><anno>LiteralFun</anno></c> typed as parameter in the function
call to a
- <seealso marker="#match_spec">match specification</seealso>.
+ <seeerl marker="#match_spec">match specification</seeerl>.
With "literal" is meant that the fun must textually be written
as the parameter of the function, it cannot be held in a
variable that in turn is passed to the function.</p>
@@ -503,8 +544,8 @@ Error: fun containing local Erlang function calls
code never calls the function, but the function call is
replaced by a literal match specification.</p>
</warning>
- <p>For more information, see <seealso marker="ms_transform#top">
- <c>ms_transform(3)</c></seealso>.</p>
+ <p>For more information, see <seeerl marker="ms_transform#top">
+ <c>ms_transform(3)</c></seeerl>.</p>
</desc>
</func>
@@ -520,7 +561,7 @@ Error: fun containing local Erlang function calls
already the owner of the table.
The calling process must be the table owner.</p>
<p>Notice that this function does not affect option
- <seealso marker="#heir"><c>heir</c></seealso> of the table. A table
+ <seeerl marker="#heir"><c>heir</c></seeerl> of the table. A table
owner can, for example, set <c>heir</c> to itself, give the table
away, and then get it back if the receiver terminates.</p>
</desc>
@@ -557,13 +598,17 @@ Error: fun containing local Erlang function calls
<item>
<p>Indicates if the table is compressed.</p>
</item>
+ <tag><c>{decentralized_counters, boolean()}</c></tag>
+ <item>
+ <p>Indicates whether the table uses <c>decentralized_counters</c>.</p>
+ </item>
<tag><c>{heir, pid() | none}</c></tag>
<item>
<p>The pid of the heir of the table, or <c>none</c> if no heir
is set.</p>
</item>
- <tag><c>{id,</c><seealso marker="#type-tid">
- <c>tid()</c></seealso><c>}</c></tag>
+ <tag><c>{id,</c><seetype marker="#tid">
+ <c>tid()</c></seetype><c>}</c></tag>
<item>
<p>The table identifier.</p>
</item>
@@ -592,8 +637,8 @@ Error: fun containing local Erlang function calls
<item>
<p>The pid of the owner of the table.</p>
</item>
- <tag><c>{protection,</c> <seealso marker="#type-access">
- <c>access()</c></seealso><c>}</c></tag>
+ <tag><c>{protection,</c> <seetype marker="#access">
+ <c>access()</c></seetype><c>}</c></tag>
<item>
<p>The table access rights.</p>
</item>
@@ -601,8 +646,8 @@ Error: fun containing local Erlang function calls
<item>
<p>The number of objects inserted in the table.</p>
</item>
- <tag><c>{type,</c> <seealso marker="#type-type">
- <c>type()</c></seealso><c>}</c></tag>
+ <tag><c>{type,</c> <seetype marker="#type">
+ <c>type()</c></seetype><c>}</c></tag>
<item>
<p>The table type.</p>
</item>
@@ -616,6 +661,13 @@ Error: fun containing local Erlang function calls
<p>Indicates whether the table uses <c>write_concurrency</c>.</p>
</item>
</taglist>
+ <note><p>The execution time of this function is affected by
+ the <seeerl marker="#new_2_decentralized_counters">
+ <c>decentralized_counters</c></seeerl> table option.
+ The execution time is much longer when the <c>decentralized_counters</c>
+ option is set to <c>true</c> than when the <c>decentralized_counters</c>
+ option is set to <c>false</c>.</p>
+ </note>
</desc>
</func>
@@ -631,7 +683,7 @@ Error: fun containing local Erlang function calls
not of the correct type, or if <c><anno>Item</anno></c> is not
one of the allowed values, a <c>badarg</c> exception is raised.</p>
<p>In addition to the <c>{<anno>Item</anno>,<anno>Value</anno>}</c>
- pairs defined for <seealso marker="#info/1"><c>info/1</c></seealso>,
+ pairs defined for <seemfa marker="#info/1"><c>info/1</c></seemfa>,
the following items are allowed:</p>
<list type="bulleted">
<item>
@@ -651,8 +703,8 @@ Error: fun containing local Erlang function calls
<p><c>Item=safe_fixed|safe_fixed_monotonic_time,
Value={FixationTime,Info}|false</c></p>
<p>If the table is fixed using
- <seealso marker="#safe_fixtable/2">
- <c>safe_fixtable/2</c></seealso>,
+ <seemfa marker="#safe_fixtable/2">
+ <c>safe_fixtable/2</c></seemfa>,
the call returns a tuple where <c>FixationTime</c> is the
last time when the table changed from unfixed to fixed.</p>
<p>The format and value of <c>FixationTime</c> depends on
@@ -661,24 +713,24 @@ Error: fun containing local Erlang function calls
<tag><c>safe_fixed</c></tag>
<item>
<p><c>FixationTime</c> corresponds to the result returned by
- <seealso marker="erts:erlang#timestamp/0">
- <c>erlang:timestamp/0</c></seealso> at the time of fixation.
+ <seemfa marker="erts:erlang#timestamp/0">
+ <c>erlang:timestamp/0</c></seemfa> at the time of fixation.
Notice that when the system uses single or multi
- <seealso marker="erts:time_correction#Time_Warp_Modes">time
- warp modes</seealso> this can produce strange results, as
+ <seeguide marker="erts:time_correction#Time_Warp_Modes">time
+ warp modes</seeguide> this can produce strange results, as
the use of <c>safe_fixed</c> is not
- <seealso marker="erts:time_correction#Time_Warp_Safe_Code">
- time warp safe</seealso>. Time warp safe code must use
+ <seeguide marker="erts:time_correction#Time_Warp_Safe_Code">
+ time warp safe</seeguide>. Time warp safe code must use
<c>safe_fixed_monotonic_time</c> instead.</p>
</item>
<tag><c>safe_fixed_monotonic_time</c></tag>
<item>
<p><c>FixationTime</c> corresponds to the result returned by
- <seealso marker="erts:erlang#monotonic_time/0">
- <c>erlang:monotonic_time/0</c></seealso> at the time of
+ <seemfa marker="erts:erlang#monotonic_time/0">
+ <c>erlang:monotonic_time/0</c></seemfa> at the time of
fixation. The use of <c>safe_fixed_monotonic_time</c> is
- <seealso marker="erts:time_correction#Time_Warp_Safe_Code">
- time warp safe</seealso>.</p>
+ <seeguide marker="erts:time_correction#Time_Warp_Safe_Code">
+ time warp safe</seeguide>.</p>
</item>
</taglist>
<p><c>Info</c> is a possibly empty lists of tuples
@@ -686,10 +738,10 @@ Error: fun containing local Erlang function calls
table is fixed by now. <c>RefCount</c> is the value
of the reference counter and it keeps track of how many times
the table has been fixed by the process.</p>
- <p>Table fixations are not limited to <seealso marker="#safe_fixtable/2">
- <c>safe_fixtable/2</c></seealso>. Temporary fixations may also
- be done by for example <seealso marker="#traversal">traversing
- functions</seealso> like <c>select</c> and <c>match</c>. Such
+ <p>Table fixations are not limited to <seemfa marker="#safe_fixtable/2">
+ <c>safe_fixtable/2</c></seemfa>. Temporary fixations may also
+ be done by for example <seeerl marker="#traversal">traversing
+ functions</seeerl> like <c>select</c> and <c>match</c>. Such
table fixations are automatically released before the
corresponding functions returns, but they may be seen by a
concurrent call to
@@ -701,6 +753,15 @@ Error: fun containing local Erlang function calls
<p>Returns internal statistics about tables on an internal format
used by OTP test suites. Not for production use.</p></item>
</list>
+ <note>
+ <p>The execution time of this function is affected by
+ the <seeerl marker="#new_2_decentralized_counters">
+ <c>decentralized_counters</c></seeerl> table option when the second
+ argument of the function is <c>size</c> or <c>memory</c>.
+ The execution time is much longer when the <c>decentralized_counters</c>
+ option is set to <c>true</c> than when the <c>decentralized_counters</c>
+ option is set to <c>false</c>.</p>
+ </note>
</desc>
</func>
@@ -714,7 +775,7 @@ Error: fun containing local Erlang function calls
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
- <seealso marker="#insert/2"><c>insert/2</c></seealso>.</p>
+ <seemfa marker="#insert/2"><c>insert/2</c></seemfa>.</p>
<p>When called with argument <c>read</c>, the function
<c><anno>InitFun</anno></c> is assumed to return
<c>end_of_input</c> when
@@ -761,7 +822,7 @@ Error: fun containing local Erlang function calls
</item>
</list>
<p>The entire operation is guaranteed to be
- <seealso marker="#concurrency">atomic and isolated</seealso>,
+ <seeerl marker="#concurrency">atomic and isolated</seeerl>,
even when a list of objects is inserted.</p>
</desc>
</func>
@@ -771,7 +832,7 @@ Error: fun containing local Erlang function calls
<fsummary>Insert an object into an ETS table if the key is not
already present.</fsummary>
<desc>
- <p>Same as <seealso marker="#insert/2"><c>insert/2</c></seealso>
+ <p>Same as <seemfa marker="#insert/2"><c>insert/2</c></seemfa>
except that instead of overwriting objects with the same key
(for <c>set</c> or <c>ordered_set</c>) or adding more objects with
keys already existing in the table (for <c>bag</c> and
@@ -781,7 +842,7 @@ Error: fun containing local Erlang function calls
inserting anything. Nothing is inserted unless
<em>all</em> keys present in the list are absent from the
table. Like <c>insert/2</c>, the entire operation is guaranteed to be
- <seealso marker="#concurrency">atomic and isolated</seealso>.</p>
+ <seeerl marker="#concurrency">atomic and isolated</seeerl>.</p>
</desc>
</func>
@@ -791,10 +852,10 @@ Error: fun containing local Erlang function calls
<c>match_spec_compile</c>.</fsummary>
<desc>
<p>Checks if a term represent a valid compiled
- <seealso marker="#match_spec">match specification</seealso>.
+ <seeerl marker="#match_spec">match specification</seeerl>.
A compiled match specifications is only valid on the Erlang node where
- it was compiled by calling <seealso marker="#match_spec_compile/1">
- <c>match_spec_compile/1</c></seealso>.</p>
+ it was compiled by calling <seemfa marker="#match_spec_compile/1">
+ <c>match_spec_compile/1</c></seemfa>.</p>
<note>
<p>
Before STDLIB 3.4 (OTP 20.0) compiled match specifications did
@@ -821,10 +882,10 @@ Error: fun containing local Erlang function calls
<p>Returns the last key <c><anno>Key</anno></c> according to Erlang
term order in table <c>Tab</c> of type <c>ordered_set</c>. For
other table types, the function is synonymous to
- <seealso marker="#first/1"><c>first/1</c></seealso>.
+ <seemfa marker="#first/1"><c>first/1</c></seemfa>.
If the table is empty, <c>'$end_of_table'</c> is returned.</p>
<p>To find preceding keys in the table, use
- <seealso marker="#prev/2"><c>prev/2</c></seealso>.</p>
+ <seemfa marker="#prev/2"><c>prev/2</c></seemfa>.</p>
</desc>
</func>
@@ -896,7 +957,7 @@ Error: fun containing local Erlang function calls
<fsummary>Continues matching objects in an ETS table.</fsummary>
<desc>
<p>Continues a match started with
- <seealso marker="#match/3"><c>match/3</c></seealso>. The next
+ <seemfa marker="#match/3"><c>match/3</c></seemfa>. The next
chunk of the size specified in the initial <c>match/3</c>
call is returned together with a new <c><anno>Continuation</anno></c>,
which can be used in subsequent calls to this function.</p>
@@ -942,20 +1003,20 @@ Error: fun containing local Erlang function calls
<fsummary>Match the objects in an ETS table against a pattern
and return part of the answers.</fsummary>
<desc>
- <p>Works like <seealso marker="#match/2"><c>match/2</c></seealso>,
+ <p>Works like <seemfa marker="#match/2"><c>match/2</c></seemfa>,
but returns only a limited (<c><anno>Limit</anno></c>) number of
matching objects. Term <c><anno>Continuation</anno></c> can then
- be used in subsequent calls to <seealso marker="#match/1">
- <c>match/1</c></seealso> to get the next chunk of matching
+ be used in subsequent calls to <seemfa marker="#match/1">
+ <c>match/1</c></seemfa> to get the next chunk of matching
objects. This is a space-efficient way to work on objects in a
table, which is faster than traversing the table object
by object using
- <seealso marker="#first/1"><c>first/1</c></seealso> and
- <seealso marker="#next/2"><c>next/2</c></seealso>.</p>
+ <seemfa marker="#first/1"><c>first/1</c></seemfa> and
+ <seemfa marker="#next/2"><c>next/2</c></seemfa>.</p>
<p>If the table is empty, <c>'$end_of_table'</c> is returned.</p>
- <p>Use <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>
- to guarantee <seealso marker="#traversal">safe traversal</seealso>
- for subsequent calls to <seealso marker="#match/1"><c>match/1</c></seealso>.</p>
+ <p>Use <seemfa marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seemfa>
+ to guarantee <seeerl marker="#traversal">safe traversal</seeerl>
+ for subsequent calls to <seemfa marker="#match/1"><c>match/1</c></seemfa>.</p>
</desc>
</func>
@@ -966,7 +1027,7 @@ Error: fun containing local Erlang function calls
<desc>
<p>Deletes all objects that match pattern <c><anno>Pattern</anno></c>
from table <c><anno>Tab</anno></c>. For a description of patterns,
- see <seealso marker="#match/2"><c>match/2</c></seealso>.</p>
+ see <seemfa marker="#match/2"><c>match/2</c></seemfa>.</p>
</desc>
</func>
@@ -975,7 +1036,7 @@ Error: fun containing local Erlang function calls
<fsummary>Continues matching objects in an ETS table.</fsummary>
<desc>
<p>Continues a match started with
- <seealso marker="#match_object/3"><c>match_object/3</c></seealso>.
+ <seemfa marker="#match_object/3"><c>match_object/3</c></seemfa>.
The next chunk of the size specified in the initial
<c>match_object/3</c> call is returned together with a new
<c><anno>Continuation</anno></c>, which can be used in subsequent
@@ -992,7 +1053,7 @@ Error: fun containing local Erlang function calls
<desc>
<p>Matches the objects in table <c><anno>Tab</anno></c> against
pattern <c><anno>Pattern</anno></c>. For a description of patterns,
- see <seealso marker="#match/2"><c>match/2</c></seealso>.
+ see <seemfa marker="#match/2"><c>match/2</c></seemfa>.
The function returns a list of all objects that
match the pattern.</p>
<p>If the key is specified in the pattern, the match is very
@@ -1009,22 +1070,22 @@ Error: fun containing local Erlang function calls
<fsummary>Match the objects in an ETS table against a pattern and
return part of the answers.</fsummary>
<desc>
- <p>Works like <seealso marker="#match_object/2">
- <c>match_object/2</c></seealso>, but only returns a
+ <p>Works like <seemfa marker="#match_object/2">
+ <c>match_object/2</c></seemfa>, but only returns a
limited (<c><anno>Limit</anno></c>) number of matching objects. Term
<c><anno>Continuation</anno></c> can then be used in subsequent
- calls to <seealso marker="#match_object/1">
- <c>match_object/1</c></seealso> to get the next chunk of matching
+ calls to <seemfa marker="#match_object/1">
+ <c>match_object/1</c></seemfa> to get the next chunk of matching
objects. This is a space-efficient way to work on objects in a
table, which is faster than traversing the table object
by object using
- <seealso marker="#first/1"><c>first/1</c></seealso> and
- <seealso marker="#next/2"><c>next/2</c></seealso>.</p>
+ <seemfa marker="#first/1"><c>first/1</c></seemfa> and
+ <seemfa marker="#next/2"><c>next/2</c></seemfa>.</p>
<p>If the table is empty, <c>'$end_of_table'</c> is returned.</p>
- <p>Use <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>
- to guarantee <seealso marker="#traversal">safe traversal</seealso>
- for subsequent calls to <seealso marker="#match_object/1">
- <c>match_object/1</c></seealso>.</p>
+ <p>Use <seemfa marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seemfa>
+ to guarantee <seeerl marker="#traversal">safe traversal</seeerl>
+ for subsequent calls to <seemfa marker="#match_object/1">
+ <c>match_object/1</c></seemfa>.</p>
</desc>
</func>
@@ -1034,19 +1095,18 @@ Error: fun containing local Erlang function calls
</fsummary>
<desc>
<p>Transforms a
- <seealso marker="#match_spec">match specification</seealso> into an
+ <seeerl marker="#match_spec">match specification</seeerl> into an
internal representation that can be used in subsequent calls to
- <seealso marker="#match_spec_run/2"><c>match_spec_run/2</c></seealso>.
+ <seemfa marker="#match_spec_run/2"><c>match_spec_run/2</c></seemfa>.
The internal representation is opaque.
To check the validity of a compiled match specification, use
- <seealso marker="#is_compiled_ms/1"><c>is_compiled_ms/1</c></seealso>.
+ <seemfa marker="#is_compiled_ms/1"><c>is_compiled_ms/1</c></seemfa>.
</p>
- <p>If term <c><anno>MatchSpec</anno></c> cannot be compiled (does not
- represent a valid match specification), a <c>badarg</c> exception is
- raised.</p>
+ <p>If term <c><anno>MatchSpec</anno></c> does not represent a valid
+ match specification, a <c>badarg</c> exception is raised.</p>
<note>
<p>This function has limited use in normal code. It is used by the
- <seealso marker="dets"><c>dets</c></seealso> module
+ <seeerl marker="dets"><c>dets</c></seeerl> module
to perform the <c>dets:select()</c> operations.</p>
</note>
</desc>
@@ -1058,10 +1118,10 @@ Error: fun containing local Erlang function calls
list of terms.</fsummary>
<desc>
<p>Executes the matching specified in a compiled
- <seealso marker="#match_spec">match specification</seealso> on a list
+ <seeerl marker="#match_spec">match specification</seeerl> on a list
of terms. Term <c><anno>CompiledMatchSpec</anno></c> is to be
- the result of a call to <seealso marker="#match_spec_compile/1">
- <c>match_spec_compile/1</c></seealso> and is hence the internal
+ the result of a call to <seemfa marker="#match_spec_compile/1">
+ <c>match_spec_compile/1</c></seemfa> and is hence the internal
representation of the match specification one wants to use.</p>
<p>The matching is executed on each element in <c><anno>List</anno></c>
and the function returns a list containing all results. If an element
@@ -1082,7 +1142,7 @@ ets:match_spec_run(ets:tab2list(Table),
ets:select(Table, MatchSpec),</code>
<note>
<p>This function has limited use in normal code. It is used by the
- <seealso marker="dets"><c>dets</c></seealso> module
+ <seeerl marker="dets"><c>dets</c></seeerl> module
to perform the <c>dets:select()</c> operations and by
Mnesia during transactions.</p>
</note>
@@ -1093,7 +1153,7 @@ ets:select(Table, MatchSpec),</code>
<name name="member" arity="2" since=""/>
<fsummary>Tests for occurrence of a key in an ETS table.</fsummary>
<desc>
- <p>Works like <seealso marker="#lookup/2"><c>lookup/2</c></seealso>,
+ <p>Works like <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa>,
but does not return the objects. Returns <c>true</c> if one or more
elements in the table has key <c><anno>Key</anno></c>, otherwise
<c>false</c>.</p>
@@ -1113,7 +1173,8 @@ ets:select(Table, MatchSpec),</code>
table is named. Default values are used for omitted options.
This means that not specifying any options (<c>[]</c>) is the same
as specifying <c>[set, protected, {keypos,1}, {heir,none},
- {write_concurrency,false}, {read_concurrency,false}]</c>.</p>
+ {write_concurrency,false}, {read_concurrency,false},
+ {decentralized_counters,false}]</c>.</p>
<taglist>
<tag><c>set</c></tag>
<item>
@@ -1172,7 +1233,7 @@ ets:select(Table, MatchSpec),</code>
<p>The function will also return the <c><anno>Name</anno></c>
instead of the table identifier. To get the table identifier of a
named table, use
- <seealso marker="#whereis/1"><c>whereis/1</c></seealso>.</p>
+ <seemfa marker="#whereis/1"><c>whereis/1</c></seemfa>.</p>
</item>
<tag><c>{keypos,<anno>Pos</anno>}</c></tag>
<item>
@@ -1208,17 +1269,27 @@ ets:select(Table, MatchSpec),</code>
(and read) by concurrent processes. This is achieved to some
degree at the expense of memory consumption and the performance
of sequential access and concurrent reading.</p>
- <p>Option <c>write_concurrency</c> can be combined with option
- <seealso marker="#new_2_read_concurrency">
- <c>read_concurrency</c></seealso>. You typically want to combine
- these when large concurrent read bursts and large concurrent
+ <p>The <c>write_concurrency</c> option can be combined with the options
+ <seeerl marker="#new_2_read_concurrency">
+ <c>read_concurrency</c></seeerl> and
+ <seeerl marker="#new_2_decentralized_counters">
+ <c>decentralized_counters</c></seeerl>. You typically want to combine
+ <c>write_concurrency</c> with <c>read_concurrency</c> when large
+ concurrent read bursts and large concurrent
write bursts are common; for more information, see option
- <seealso marker="#new_2_read_concurrency">
- <c>read_concurrency</c></seealso>.</p>
+ <seeerl marker="#new_2_read_concurrency">
+ <c>read_concurrency</c></seeerl>. The <c>decentralized_counters</c>
+ option is turned on by default for tables of type <c>ordered_set</c>
+ with the <c>write_concurrency</c> option enabled, and the
+ <c>decentralized_counters</c> option is turned off by default for
+ all other table types.
+ For more information, see the documentation for the
+ <seeerl marker="#new_2_decentralized_counters">
+ <c>decentralized_counters</c></seeerl> option.</p>
<p>Notice that this option does not change any guarantees about
- <seealso marker="#concurrency">atomicity and isolation</seealso>.
+ <seeerl marker="#concurrency">atomicity and isolation</seeerl>.
Functions that makes such promises over many objects (like
- <seealso marker="#insert/2"><c>insert/2</c></seealso>)
+ <seemfa marker="#insert/2"><c>insert/2</c></seemfa>)
gain less (or nothing) from this option.</p>
<p>The memory consumption inflicted by both <c>write_concurrency</c>
and <c>read_concurrency</c> is a constant overhead per table for
@@ -1252,11 +1323,43 @@ ets:select(Table, MatchSpec),</code>
operations repeatedly. In this case, you would get a performance
degradation by enabling this option.</p>
<p>Option <c>read_concurrency</c> can be combined with option
- <seealso marker="#new_2_write_concurrency">
- <c>write_concurrency</c></seealso>.
+ <seeerl marker="#new_2_write_concurrency">
+ <c>write_concurrency</c></seeerl>.
You typically want to combine these when large concurrent
read bursts and large concurrent write bursts are common.</p>
- <marker id="new_2_compressed"></marker>
+ <marker id="new_2_decentralized_counters"></marker>
+ </item>
+ <tag><c>{decentralized_counters,boolean()}</c></tag>
+ <item>
+ <p>
+ Performance tuning. Defaults to <c>true</c> for tables
+ of type <c>ordered_set</c> with the
+ <seeerl marker="#new_2_write_concurrency">
+ <c>write_concurrency</c></seeerl> option enabled, and defaults to
+ false for all other table types. This option has no effect if
+ the <c>write_concurrency</c> option is set to <c>false</c>.</p>
+ <p>
+ When this option is set to <c>true</c>, the table is optimized for
+ frequent concurrent calls to operations that modify the tables
+ size and/or its memory consumption (e.g., <seemfa
+ marker="#insert/2"><c>insert/2</c></seemfa> and <seemfa
+ marker="#delete/2"><c>delete/2</c></seemfa>).
+ The drawback is that calls to
+ <seemfa marker="#info/1"><c>info/1</c></seemfa> and
+ <seemfa marker="#info/2"><c>info/2</c></seemfa> with <c>size</c> or
+ <c>memory</c> as the second argument can get much slower when the
+ <c>decentralized_counters</c> option is turned on.</p>
+ <p>
+ When this option is enabled the counters for the
+ table size and memory consumption are distributed over
+ several cache lines and the scheduling threads are
+ mapped to one of those cache lines. The <c>erl</c>
+ option <seecom
+ marker="erts:erl#+dcg"><c>+dcg</c></seecom> can be used
+ to control the number of cache lines that the counters
+ are distributed over.
+ </p>
+ <marker id="new_2_compressed"></marker>
</item>
<tag><c>compressed</c></tag>
<item>
@@ -1281,10 +1384,10 @@ ets:select(Table, MatchSpec),</code>
according to the internal order of the table is returned. If no
next key exists, <c>'$end_of_table'</c> is returned.</p>
<p>To find the first key in the table, use
- <seealso marker="#first/1"><c>first/1</c></seealso>.</p>
+ <seemfa marker="#first/1"><c>first/1</c></seemfa>.</p>
<p>Unless a table of type <c>set</c>, <c>bag</c>, or
<c>duplicate_bag</c> is fixated using
- <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>,
+ <seemfa marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seemfa>,
a call to <c>next/2</c> will fail if <c><anno>Key1</anno></c> no longer
exists in the table. For table type <c>ordered_set</c>, the function
always returns the next key after <c><anno>Key1</anno></c> in term
@@ -1302,10 +1405,10 @@ ets:select(Table, MatchSpec),</code>
<c><anno>Key1</anno></c> according to Erlang term order in table
<c><anno>Tab</anno></c> of type <c>ordered_set</c>. For other
table types, the function is synonymous to
- <seealso marker="#next/2"><c>next/2</c></seealso>.
+ <seemfa marker="#next/2"><c>next/2</c></seemfa>.
If no previous key exists, <c>'$end_of_table'</c> is returned.</p>
<p>To find the last key in an <c>ordered_set</c> table, use
- <seealso marker="#last/1"><c>last/1</c></seealso>.</p>
+ <seemfa marker="#last/1"><c>last/1</c></seemfa>.</p>
</desc>
</func>
@@ -1325,8 +1428,8 @@ ets:select(Table, MatchSpec),</code>
that has passed through external representation.</fsummary>
<desc>
<p>Restores an opaque continuation returned by
- <seealso marker="#select/3"><c>select/3</c></seealso> or
- <seealso marker="#select/1"><c>select/1</c></seealso> if the
+ <seemfa marker="#select/3"><c>select/3</c></seemfa> or
+ <seemfa marker="#select/1"><c>select/1</c></seemfa> if the
continuation has passed through external term format (been
sent between nodes or stored on disk).</p>
<p>The reason for this function is that continuation terms
@@ -1364,7 +1467,7 @@ ets:select(ets:repair_continuation(MaybeBroken,MS)).</code>
<p>The actual behavior of compiled match specifications when recreated
from external format has changed and may change in future releases,
but this interface remains for backward compatibility.
- See <seealso marker="#is_compiled_ms/1"><c>is_compiled_ms/1</c></seealso>.</p>
+ See <seemfa marker="#is_compiled_ms/1"><c>is_compiled_ms/1</c></seemfa>.</p>
</note>
</desc>
</func>
@@ -1374,16 +1477,16 @@ ets:select(ets:repair_continuation(MaybeBroken,MS)).</code>
<fsummary>Fix an ETS table for safe traversal.</fsummary>
<desc>
<p>Fixes a table of type <c>set</c>, <c>bag</c>, or
- <c>duplicate_bag</c> for <seealso marker="#traversal">
- safe traversal</seealso> using
- <seealso marker="#first/1"><c>first/1</c></seealso> &amp;
- <seealso marker="#next/2"><c>next/2</c></seealso>,
- <seealso marker="#match/3"><c>match/3</c></seealso> &amp;
- <seealso marker="#match/1"><c>match/1</c></seealso>,
- <seealso marker="#match_object/3"><c>match_object/3</c></seealso> &amp;
- <seealso marker="#match_object/1"><c>match_object/1</c></seealso>, or
- <seealso marker="#select/3"><c>select/3</c></seealso> &amp;
- <seealso marker="#select/1"><c>select/1</c></seealso>.</p>
+ <c>duplicate_bag</c> for <seeerl marker="#traversal">
+ safe traversal</seeerl> using
+ <seemfa marker="#first/1"><c>first/1</c></seemfa> &amp;
+ <seemfa marker="#next/2"><c>next/2</c></seemfa>,
+ <seemfa marker="#match/3"><c>match/3</c></seemfa> &amp;
+ <seemfa marker="#match/1"><c>match/1</c></seemfa>,
+ <seemfa marker="#match_object/3"><c>match_object/3</c></seemfa> &amp;
+ <seemfa marker="#match_object/1"><c>match_object/1</c></seemfa>, or
+ <seemfa marker="#select/3"><c>select/3</c></seemfa> &amp;
+ <seemfa marker="#select/1"><c>select/1</c></seemfa>.</p>
<p>A process fixes a table by calling
<c>safe_fixtable(<anno>Tab</anno>, true)</c>. The table remains
fixed until the process releases it by calling
@@ -1394,8 +1497,8 @@ ets:select(ets:repair_continuation(MaybeBroken,MS)).</code>
A reference counter is kept on a per process basis, and N
consecutive fixes requires N releases to release the table.</p>
<p>When a table is fixed, a sequence of
- <seealso marker="#first/1"><c>first/1</c></seealso> and
- <seealso marker="#next/2"><c>next/2</c></seealso> calls are
+ <seemfa marker="#first/1"><c>first/1</c></seemfa> and
+ <seemfa marker="#next/2"><c>next/2</c></seemfa> calls are
guaranteed to succeed even if keys are removed during the
traversal. The keys for objects inserted or deleted during a
traversal may or may not be returned by <c>next/2</c> depending on
@@ -1424,13 +1527,13 @@ clean_all_with_value(Tab,X,Key) ->
objects is never freed. The performance of operations on
the table also degrades significantly.</p>
<p>To retrieve information about which processes have fixed which
- tables, use <seealso marker="#info_2_safe_fixed_monotonic_time">
- <c>info(Tab, safe_fixed_monotonic_time)</c></seealso>. A system with
+ tables, use <seeerl marker="#info_2_safe_fixed_monotonic_time">
+ <c>info(Tab, safe_fixed_monotonic_time)</c></seeerl>. A system with
many processes fixing tables can need a monitor that sends alarms
when tables have been fixed for too long.</p>
<p>Notice that <c>safe_fixtable/2</c> is not necessary for table type
<c>ordered_set</c> and for traversals done by a single ETS function call,
- like <seealso marker="#select/2"><c>select/2</c></seealso>.</p>
+ like <seemfa marker="#select/2"><c>select/2</c></seemfa>.</p>
</desc>
</func>
@@ -1439,7 +1542,7 @@ clean_all_with_value(Tab,X,Key) ->
<fsummary>Continue matching objects in an ETS table.</fsummary>
<desc>
<p>Continues a match started with
- <seealso marker="#select/3"><c>select/3</c></seealso>. The next
+ <seemfa marker="#select/3"><c>select/3</c></seemfa>. The next
chunk of the size specified in the initial <c>select/3</c>
call is returned together with a new <c><anno>Continuation</anno></c>,
which can be used in subsequent calls to this function.</p>
@@ -1454,10 +1557,10 @@ clean_all_with_value(Tab,X,Key) ->
match specification.</fsummary>
<desc>
<p>Matches the objects in table <c><anno>Tab</anno></c> using a
- <seealso marker="#match_spec">match specification</seealso>.
+ <seeerl marker="#match_spec">match specification</seeerl>.
This is a more general call than
- <seealso marker="#match/2"><c>match/2</c></seealso> and
- <seealso marker="#match_object/2"><c>match_object/2</c></seealso>
+ <seemfa marker="#match/2"><c>match/2</c></seemfa> and
+ <seemfa marker="#match_object/2"><c>match_object/2</c></seemfa>
calls. In its simplest form, the match specification is as
follows:</p>
<code type="none">
@@ -1469,7 +1572,7 @@ Result = "Term construct"</code>
<p>This means that the match specification is always a list of one or
more tuples (of arity 3). The first element of the tuple is to be
a pattern as described in
- <seealso marker="#match/2"><c>match/2</c></seealso>.
+ <seemfa marker="#match/2"><c>match/2</c></seemfa>.
The second element of the tuple is to
be a list of 0 or more guard tests (described below). The
third element of the tuple is to be a list containing a
@@ -1512,8 +1615,8 @@ ets:select(Tab,[{{'$1','$2','$1'},[],['$_']}])</code>
ets:select(Tab,[{{'$1','$2','$1'},[],[{{'$1','$2','$3'}}]}])</code>
<p>This syntax is equivalent to the syntax used in the trace
patterns (see the
- <seealso marker="runtime_tools:dbg">
- <c>dbg(3)</c></seealso>) module in Runtime_Tools.</p>
+ <seeerl marker="runtime_tools:dbg">
+ <c>dbg(3)</c></seeerl>) module in Runtime_Tools.</p>
<p>The <c>Guard</c>s are constructed as tuples, where the first
element is the test name and the remaining elements
are the test parameters. To check for a specific type
@@ -1548,20 +1651,20 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
<fsummary>Match the objects in an ETS table against a match
specification and return part of the answers.</fsummary>
<desc>
- <p>Works like <seealso marker="#select/2"><c>select/2</c></seealso>,
+ <p>Works like <seemfa marker="#select/2"><c>select/2</c></seemfa>,
but only returns a limited
(<c><anno>Limit</anno></c>) number of matching objects. Term
<c><anno>Continuation</anno></c> can then be used in subsequent
- calls to <seealso marker="#select/1"><c>select/1</c></seealso>
+ calls to <seemfa marker="#select/1"><c>select/1</c></seemfa>
to get the next chunk of matching
objects. This is a space-efficient way to work on objects in a
table, which is still faster than traversing the table object by
- object using <seealso marker="#first/1"><c>first/1</c></seealso>
- and <seealso marker="#next/2"><c>next/2</c></seealso>.</p>
+ object using <seemfa marker="#first/1"><c>first/1</c></seemfa>
+ and <seemfa marker="#next/2"><c>next/2</c></seemfa>.</p>
<p>If the table is empty, <c>'$end_of_table'</c> is returned.</p>
- <p>Use <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>
- to guarantee <seealso marker="#traversal">safe traversal</seealso>
- for subsequent calls to <seealso marker="#select/1"><c>select/1</c></seealso>.</p>
+ <p>Use <seemfa marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seemfa>
+ to guarantee <seeerl marker="#traversal">safe traversal</seeerl>
+ for subsequent calls to <seemfa marker="#select/1"><c>select/1</c></seemfa>.</p>
</desc>
</func>
@@ -1572,13 +1675,13 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
specification returned <c>true</c>.</fsummary>
<desc>
<p>Matches the objects in table <c><anno>Tab</anno></c> using a
- <seealso marker="#match_spec">match specification</seealso>. If the
+ <seeerl marker="#match_spec">match specification</seeerl>. If the
match specification returns <c>true</c> for an object, that object
considered a match and is counted. For any other result from
the match specification the object is not considered a match and is
therefore not counted.</p>
<p>This function can be described as a
- <seealso marker="#match_delete/2"><c>match_delete/2</c></seealso>
+ <seemfa marker="#match_delete/2"><c>match_delete/2</c></seemfa>
function that does not delete any elements, but only counts them.</p>
<p>The function returns the number of objects matched.</p>
</desc>
@@ -1591,12 +1694,12 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
returns <c>true</c>.</fsummary>
<desc>
<p>Matches the objects in table <c><anno>Tab</anno></c> using a
- <seealso marker="#match_spec">match specification</seealso>. If the
+ <seeerl marker="#match_spec">match specification</seeerl>. If the
match specification returns <c>true</c> for an object, that object is
removed from the table. For any other result from the match
specification the object is retained. This is a more general
- call than the <seealso marker="#match_delete/2">
- <c>match_delete/2</c></seealso> call.</p>
+ call than the <seemfa marker="#match_delete/2">
+ <c>match_delete/2</c></seemfa> call.</p>
<p>The function returns the number of objects
deleted from the table.</p>
<note>
@@ -1613,11 +1716,11 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
<fsummary>Match and replace objects atomically in an ETS table</fsummary>
<desc>
<p>Matches the objects in the table <c><anno>Tab</anno></c> using a
- <seealso marker="#match_spec">match specification</seealso>. For each
+ <seeerl marker="#match_spec">match specification</seeerl>. For each
matched object, the existing object is replaced with
the match specification result.</p>
<p>The match-and-replace operation for each individual object is guaranteed to be
- <seealso marker="#concurrency">atomic and isolated</seealso>. The
+ <seeerl marker="#concurrency">atomic and isolated</seeerl>. The
<c>select_replace</c> table traversal as a whole, like all other select functions,
does not give such guarantees.</p>
<p>The match specifiction must be guaranteed to <em>retain the key</em>
@@ -1651,13 +1754,13 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<name name="select_reverse" arity="1" since="OTP R14B"/>
<fsummary>Continue matching objects in an ETS table.</fsummary>
<desc>
- <p>Continues a match started with <seealso marker="#select_reverse/3">
- <c>select_reverse/3</c></seealso>. For tables of type
+ <p>Continues a match started with <seemfa marker="#select_reverse/3">
+ <c>select_reverse/3</c></seemfa>. For tables of type
<c>ordered_set</c>, the traversal of the table continues
to objects with keys earlier in the Erlang term order. The
returned list also contains objects with keys in reverse order.
For all other table types, the behavior is exactly that of
- <seealso marker="#select/1"><c>select/1</c></seealso>.</p>
+ <seemfa marker="#select/1"><c>select/1</c></seemfa>.</p>
<p><em>Example:</em></p>
<code>
1> T = ets:new(x,[ordered_set]).
@@ -1685,7 +1788,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<fsummary>Match the objects in an ETS table against a
match specification.</fsummary>
<desc>
- <p>Works like <seealso marker="#select/2"><c>select/2</c></seealso>,
+ <p>Works like <seemfa marker="#select/2"><c>select/2</c></seemfa>,
but returns the list in reverse order for table type <c>ordered_set</c>.
For all other table types, the return value is identical to that of
<c>select/2</c>.</p>
@@ -1697,7 +1800,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<fsummary>Match the objects in an ETS table against a
match specification and return part of the answers.</fsummary>
<desc>
- <p>Works like <seealso marker="#select/3"><c>select/3</c></seealso>,
+ <p>Works like <seemfa marker="#select/3"><c>select/3</c></seemfa>,
but for table type <c>ordered_set</c>
traversing is done starting at the last object in
Erlang term order and moves to the first. For all other table
@@ -1716,7 +1819,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<desc>
<p>Sets table options. The only allowed option to be set after the
table has been created is
- <seealso marker="#heir"><c>heir</c></seealso>.
+ <seeerl marker="#heir"><c>heir</c></seeerl>.
The calling process must be the table owner.</p>
</desc>
</func>
@@ -1738,7 +1841,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
the function fails with reason <c>badarg</c>.</p>
<p>Unless a table of type <c>set</c>, <c>bag</c>, or
<c>duplicate_bag</c> is protected using
- <seealso marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seealso>,
+ <seemfa marker="#safe_fixtable/2"><c>safe_fixtable/2</c></seemfa>,
a traversal can fail if
concurrent updates are made to the table. For table type
<c>ordered_set</c>, the function returns a list containing
@@ -1817,8 +1920,8 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<fsummary>Return a list of all objects in an ETS table.</fsummary>
<desc>
<p>Returns information about the table dumped to file by
- <seealso marker="#tab2file/2"><c>tab2file/2</c></seealso> or
- <seealso marker="#tab2file/3"><c>tab2file/3</c></seealso>.</p>
+ <seemfa marker="#tab2file/2"><c>tab2file/2</c></seemfa> or
+ <seemfa marker="#tab2file/3"><c>tab2file/3</c></seemfa>.</p>
<p>The following items are returned:</p>
<taglist>
<tag><c>name</c></tag>
@@ -1826,7 +1929,7 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<p>The name of the dumped table. If the table was a
named table, a table with the same name cannot exist when the
table is loaded from file with
- <seealso marker="#file2tab/2"><c>file2tab/2</c></seealso>.
+ <seemfa marker="#file2tab/2"><c>file2tab/2</c></seemfa>.
If the table is
not saved as a named table, this field has no significance
when loading the table from file.</p>
@@ -1867,8 +1970,8 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<item>
<p>The extended information written in the file footer to
allow stronger verification during table loading from file, as
- specified to <seealso marker="#tab2file/3">
- <c>tab2file/3</c></seealso>. Notice that this
+ specified to <seemfa marker="#tab2file/3">
+ <c>tab2file/3</c></seemfa>. Notice that this
function only tells <em>which</em> information is present, not
the values in the file footer. The value is a list containing one
or more of the atoms <c>object_count</c> and <c>md5sum</c>.</p>
@@ -1885,8 +1988,8 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
</taglist>
<p>An error is returned if the file is inaccessible,
badly damaged, or not produced with
- <seealso marker="#tab2file/2"><c>tab2file/2</c></seealso> or
- <seealso marker="#tab2file/3"><c>tab2file/3</c></seealso>.</p>
+ <seemfa marker="#tab2file/2"><c>tab2file/2</c></seemfa> or
+ <seemfa marker="#tab2file/3"><c>tab2file/3</c></seemfa>.</p>
</desc>
</func>
@@ -1897,14 +2000,14 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<desc>
<p>Returns a Query List
Comprehension (QLC) query handle. The
- <seealso marker="qlc"><c>qlc</c></seealso> module provides
+ <seeerl marker="qlc"><c>qlc</c></seeerl> module provides
a query language aimed mainly at Mnesia, but ETS
tables, Dets tables,
and lists are also recognized by QLC as sources of
data. Calling <c>table/1,2</c> is the means to make the
ETS table <c>Tab</c> usable to QLC.</p>
<p>When there are only simple restrictions on the key position,
- QLC uses <seealso marker="#lookup/2"><c>lookup/2</c></seealso>
+ QLC uses <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa>
to look up the keys. When
that is not possible, the whole table is traversed.
Option <c>traverse</c> determines how this is done:</p>
@@ -1912,24 +2015,24 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<tag><c>first_next</c></tag>
<item>
<p>The table is traversed one key at a time by calling
- <seealso marker="#first/1"><c>first/1</c></seealso> and
- <seealso marker="#next/2"><c>next/2</c></seealso>.</p>
+ <seemfa marker="#first/1"><c>first/1</c></seemfa> and
+ <seemfa marker="#next/2"><c>next/2</c></seemfa>.</p>
</item>
<tag><c>last_prev</c></tag>
<item>
<p>The table is traversed one key at a time by calling
- <seealso marker="#last/1"><c>last/1</c></seealso> and
- <seealso marker="#prev/2"><c>prev/2</c></seealso>.</p>
+ <seemfa marker="#last/1"><c>last/1</c></seemfa> and
+ <seemfa marker="#prev/2"><c>prev/2</c></seemfa>.</p>
</item>
<tag><c>select</c></tag>
<item>
<p>The table is traversed by calling
- <seealso marker="#select/3"><c>select/3</c></seealso> and
- <seealso marker="#select/1"><c>select/1</c></seealso>.
+ <seemfa marker="#select/3"><c>select/3</c></seemfa> and
+ <seemfa marker="#select/1"><c>select/1</c></seemfa>.
Option <c>n_objects</c> determines the number of objects
returned (the third argument of <c>select/3</c>); the
default is to return <c>100</c> objects at a time. The
- <seealso marker="#match_spec">match specification</seealso> (the
+ <seeerl marker="#match_spec">match specification</seeerl> (the
second argument of <c>select/3</c>) is assembled by QLC: simple
filters are translated into equivalent match specifications
while more complicated filters must be applied to all
@@ -1939,8 +2042,8 @@ Success = (1 =:= ets:select_replace(T, [{Old, [], [{const, New}]}])),
<tag><c>{select, <anno>MatchSpec</anno>}</c></tag>
<item>
<p>As for <c>select</c>, the table is traversed by calling
- <seealso marker="#select/3"><c>select/3</c></seealso> and
- <seealso marker="#select/1"><c>select/1</c></seealso>.
+ <seemfa marker="#select/3"><c>select/3</c></seemfa> and
+ <seemfa marker="#select/1"><c>select/1</c></seemfa>.
The difference is that the match specification is explicitly
specified. This is how to state match specifications that cannot
easily be expressed within the syntax provided by QLC.</p>
@@ -1978,8 +2081,8 @@ true</pre>
by either <em>comparing equal</em> the key of an object in an
<c>ordered_set</c> table, or <em>matching</em> in other types of
tables (for details on the difference, see
- <seealso marker="#lookup/2"><c>lookup/2</c></seealso> and
- <seealso marker="#new/2"><c>new/2</c></seealso>).</p>
+ <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa> and
+ <seemfa marker="#new/2"><c>new/2</c></seemfa>).</p>
</desc>
</func>
<func>
@@ -1988,8 +2091,8 @@ true</pre>
</fsummary>
<desc>
<p>This function is a utility to test a
- <seealso marker="#match_spec">match specification</seealso> used in
- calls to <seealso marker="#select/2"><c>select/2</c></seealso>.
+ <seeerl marker="#match_spec">match specification</seeerl> used in
+ calls to <seemfa marker="#select/2"><c>select/2</c></seemfa>.
The function both tests <c><anno>MatchSpec</anno></c> for "syntactic"
correctness and runs the match specification against object
<c><anno>Tuple</anno></c>.</p>
@@ -2004,8 +2107,8 @@ true</pre>
descriptions of what was wrong with the match specification.</p>
<p>This is a useful debugging and test tool, especially when
writing complicated <c>select/2</c> calls.</p>
- <p>See also: <seealso marker="erts:erlang#match_spec_test/3">
- erlang:match_spec_test/3</seealso>.</p>
+ <p>See also: <seemfa marker="erts:erlang#match_spec_test/3">
+ erlang:match_spec_test/3</seemfa>.</p>
</desc>
</func>
@@ -2040,7 +2143,7 @@ true</pre>
counters, without the trouble of having to look up an object, update
the object by incrementing an element, and insert the resulting
object into the table again. The operation is guaranteed to be
- <seealso marker="#concurrency">atomic and isolated</seealso>.</p>
+ <seeerl marker="#concurrency">atomic and isolated</seeerl>.</p>
<p>This function destructively update the object with key
<c><anno>Key</anno></c> in table <c><anno>Tab</anno></c> by adding
<c><anno>Incr</anno></c> to the element at position
@@ -2074,8 +2177,8 @@ true</pre>
by either <em>matching</em> the key of an object in a <c>set</c>
table, or <em>compare equal</em> to the key of an object in an
<c>ordered_set</c> table (for details on the difference, see
- <seealso marker="#lookup/2"><c>lookup/2</c></seealso> and
- <seealso marker="#new/2"><c>new/2</c></seealso>).</p>
+ <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa> and
+ <seemfa marker="#new/2"><c>new/2</c></seemfa>).</p>
<p>If a default object <c><anno>Default</anno></c> is specified,
it is used
as the object to be updated if the key is missing from the table. The
@@ -2133,8 +2236,8 @@ true</pre>
by either <em>matching</em> the key of an object in a <c>set</c>
table, or <em>compare equal</em> to the key of an object in an
<c>ordered_set</c> table (for details on the difference, see
- <seealso marker="#lookup/2"><c>lookup/2</c></seealso> and
- <seealso marker="#new/2"><c>new/2</c></seealso>).</p>
+ <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa> and
+ <seemfa marker="#new/2"><c>new/2</c></seemfa>).</p>
<p>The function fails with reason <c>badarg</c> in the following
situations:</p>
<list type="bulleted">
@@ -2151,7 +2254,7 @@ true</pre>
<fsummary>Retrieves the tid() of a named table.</fsummary>
<desc>
<p>This function returns the
- <seealso marker="#type-tid"><c>tid()</c></seealso> of the named table
+ <seetype marker="#tid"><c>tid()</c></seetype> of the named table
identified by <c><anno>TableName</anno></c>, or <c>undefined</c> if
no such table exists. The <c>tid()</c> can be used in place of the
table name in all operations, which is slightly faster since the name
diff --git a/lib/stdlib/doc/src/file_sorter.xml b/lib/stdlib/doc/src/file_sorter.xml
index 0bd2399bff..3a90ade7dd 100644
--- a/lib/stdlib/doc/src/file_sorter.xml
+++ b/lib/stdlib/doc/src/file_sorter.xml
@@ -242,7 +242,7 @@ output(L) ->
<item>
<p><c>{file_error, FileName, file:posix()}</c> - For an
explanation of <c>file:posix()</c>, see
- <seealso marker="kernel:file"><c>file(3)</c></seealso>.</p>
+ <seeerl marker="kernel:file"><c>file(3)</c></seeerl>.</p>
</item>
<item>
<p><c>{premature_eof, FileName}</c> - End-of-file was
diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml
index 5df415834f..75040e03de 100644
--- a/lib/stdlib/doc/src/filelib.xml
+++ b/lib/stdlib/doc/src/filelib.xml
@@ -37,14 +37,14 @@
</modulesummary>
<description>
<p>This module contains utilities on a higher level than the
- <seealso marker="kernel:file"><c>file</c></seealso> module.</p>
+ <seeerl marker="kernel:file"><c>file</c></seeerl> module.</p>
<p>This module does not support "raw" filenames (that is, files whose
names do not comply with the expected encoding). Such files are ignored
by the functions in this module.</p>
<p>For more information about raw filenames, see the
- <seealso marker="kernel:file"><c>file</c></seealso> module.</p>
+ <seeerl marker="kernel:file"><c>file</c></seeerl> module.</p>
<note>
<p>
@@ -123,7 +123,7 @@
<c><anno>F</anno></c> in directory <c><anno>Dir</anno></c> that match
the regular expression <c><anno>RegExp</anno></c> (for a description
of the allowed regular expressions,
- see the <seealso marker="re"><c>re</c></seealso> module).
+ see the <seeerl marker="re"><c>re</c></seeerl> module).
If <c><anno>Recursive</anno></c> is <c>true</c>, all subdirectories
to <c>Dir</c>
are processed. The regular expression matching is only done on
@@ -137,7 +137,7 @@
not conform to the expected character encoding (that is, are not
encoded in valid UTF-8).</p>
<p>For more information about raw filenames, see the
- <seealso marker="kernel:file"><c>file</c></seealso> module.</p>
+ <seeerl marker="kernel:file"><c>file</c></seeerl> module.</p>
</desc>
</func>
@@ -256,7 +256,7 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code>
<fsummary>Match filenames using Unix-style wildcards starting at a
specified directory.</fsummary>
<desc>
- <p>Same as <seealso marker="#wildcard/1"><c>wildcard/1</c></seealso>,
+ <p>Same as <seemfa marker="#wildcard/1"><c>wildcard/1</c></seemfa>,
except that <c><anno>Cwd</anno></c> is used instead of the working
directory.</p>
</desc>
@@ -273,8 +273,8 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code>
corresponding path ending in <c>"src"</c> should be searched.</p>
<p>If <c><anno>Rules</anno></c> is left out or is an empty list, the
default system rules are used. See also the Kernel application
- parameter <seealso
- marker="kernel:kernel_app#source_search_rules"><c>source_search_rules</c></seealso>.</p>
+ parameter <seeapp
+ marker="kernel:kernel_app#source_search_rules"><c>source_search_rules</c></seeapp>.</p>
</desc>
</func>
<func>
@@ -297,14 +297,44 @@ filelib:wildcard("lib/**/*.{erl,hrl}")</code>
look for a file with a corresponding extension <c>.erl</c> by
replacing the suffix <c>"ebin"</c> of the object directory path with
<c>"src"</c> or <c>"src/*"</c>.
- The file search is done through <seealso
- marker="#find_file/3"><c>find_file/3</c></seealso>. The directory of
+ The file search is done through <seemfa
+ marker="#find_file/3"><c>find_file/3</c></seemfa>. The directory of
the object file is always tried before any other directory specified
by the rules.</p>
<p>If <c><anno>Rules</anno></c> is left out or is an empty list, the
default system rules are used. See also the Kernel application
- parameter <seealso
- marker="kernel:kernel_app#source_search_rules"><c>source_search_rules</c></seealso>.</p>
+ parameter <seeapp
+ marker="kernel:kernel_app#source_search_rules"><c>source_search_rules</c></seeapp>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="safe_relative_path" arity="2" since="OTP 23.0"/>
+ <fsummary>Sanitize a relative path to avoid directory traversal attacks.</fsummary>
+ <desc>
+ <p>Sanitizes the relative path by eliminating ".." and "."
+ components to protect against directory traversal attacks.
+ Either returns the sanitized path name, or the atom
+ <c>unsafe</c> if the path is unsafe.
+ The path is considered unsafe in the following circumstances:</p>
+ <list type="bulleted">
+ <item><p>The path is not relative.</p></item>
+ <item><p>A ".." component would climb up above the root of
+ the relative path.</p></item>
+ <item><p>A symbolic link in the path points above the root
+ of the relative path.</p></item>
+ </list>
+ <p><em>Examples:</em></p>
+ <pre>
+1> <input>{ok, Cwd} = file:get_cwd().</input>
+...
+2> <input>filelib:safe_relative_path("dir/sub_dir/..", Cwd).</input>
+"dir"
+3> <input>filelib:safe_relative_path("dir/..", Cwd).</input>
+[]
+4> <input>filelib:safe_relative_path("dir/../..", Cwd).</input>
+unsafe
+5> <input>filelib:safe_relative_path("/abs/path", Cwd).</input>
+unsafe</pre>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml
index 70a1e60499..ec2ff0f40c 100644
--- a/lib/stdlib/doc/src/filename.xml
+++ b/lib/stdlib/doc/src/filename.xml
@@ -44,21 +44,21 @@
<p>In Windows, all functions return filenames with forward slashes
only, even if the arguments contain backslashes. To normalize a
filename by removing redundant directory separators, use
- <seealso marker="#join/1"><c>join/1</c></seealso>.</p>
+ <seemfa marker="#join/1"><c>join/1</c></seemfa>.</p>
<p>
The module supports
- <seealso marker="unicode_usage#notes-about-raw-filenames">raw
- filenames</seealso> in the way that if a binary is
+ <seeguide marker="unicode_usage#notes-about-raw-filenames">raw
+ filenames</seeguide> in the way that if a binary is
present, or the filename cannot be interpreted according to the return
- value of <seealso marker="kernel:file#native_name_encoding/0">
- <c>file:native_name_encoding/0</c></seealso>, a raw filename is also
+ value of <seemfa marker="kernel:file#native_name_encoding/0">
+ <c>file:native_name_encoding/0</c></seemfa>, a raw filename is also
returned. For example, <c>join/1</c> provided with a path component
that is a binary (and cannot be interpreted under the current
native filename encoding) results in a raw filename that is returned
(the join operation is performed of course). For more information
about raw filenames, see the
- <seealso marker="kernel:file"><c>file</c></seealso> module.</p>
+ <seeerl marker="kernel:file"><c>file</c></seeerl> module.</p>
<note>
<p>
@@ -123,7 +123,7 @@
<fsummary>Convert a filename to an absolute name, relative a specified
directory.</fsummary>
<desc>
- <p>Same as <seealso marker="#absname/1"><c>absname/1</c></seealso>,
+ <p>Same as <seemfa marker="#absname/1"><c>absname/1</c></seemfa>,
except that the directory to which the filename is to be made
relative is specified in argument <c><anno>Dir</anno></c>.</p>
</desc>
@@ -134,7 +134,7 @@
<fsummary>Join an absolute directory with a relative filename.</fsummary>
<desc>
<p>Joins an absolute directory with a relative filename. Similar to
- <seealso marker="#join/2"><c>join/2</c></seealso>, but on platforms
+ <seemfa marker="#join/2"><c>join/2</c></seemfa>, but on platforms
with tight restrictions on raw filename length and no support for
symbolic links (read: VxWorks), leading parent directory components
in <c><anno>Filename</anno></c> are matched against trailing
@@ -157,10 +157,10 @@
<type variable="Application"/>
<desc>
<p>
- Equivalent to <seealso marker="#basedir_3_1">
- basedir(<anno>PathType</anno>, <anno>Application</anno>, #{})</seealso>
- or <seealso marker="#basedir_3_2">
-basedir(<anno>PathsType</anno>, <anno>Application</anno>, #{})</seealso>.
+ Equivalent to <seeerl marker="#basedir_3_1">
+ basedir(<anno>PathType</anno>, <anno>Application</anno>, #{})</seeerl>
+ or <seeerl marker="#basedir_3_2">
+basedir(<anno>PathsType</anno>, <anno>Application</anno>, #{})</seeerl>.
</p>
</desc>
</func>
@@ -395,12 +395,12 @@ true
<fsummary>Find the filename and compiler options for a module.</fsummary>
<desc>
<p>Finds the source filename and compiler options for a module.
- The result can be fed to <seealso marker="compiler:compile#file/2">
- <c>compile:file/2</c></seealso> to compile the file again.</p>
+ The result can be fed to <seemfa marker="compiler:compile#file/2">
+ <c>compile:file/2</c></seemfa> to compile the file again.</p>
<warning>
- <p>This function is deprecated. Use <seealso marker="filelib#find_source/1">
- <c>filelib:find_source/1</c></seealso> instead for finding source files.</p>
- <p>If possible, use the <seealso marker="beam_lib"><c>beam_lib(3)</c></seealso>
+ <p>This function is deprecated. Use <seemfa marker="filelib#find_source/1">
+ <c>filelib:find_source/1</c></seemfa> instead for finding source files.</p>
+ <p>If possible, use the <seeerl marker="beam_lib"><c>beam_lib(3)</c></seeerl>
module to extract the compiler options and the abstract code
format from the Beam file and compile that instead.</p></warning>
<p>Argument <c><anno>Beam</anno></c>, which can be a string or an atom,
@@ -415,8 +415,8 @@ true
object is located matches <c><anno>BinSuffix</anno></c>, then the
name created by replacing <c><anno>BinSuffix</anno></c> with
<c><anno>SourceSuffix</anno></c> is expanded by calling
- <seealso marker="filelib#wildcard/1">
- <c>filelib:wildcard/1</c></seealso>.
+ <seemfa marker="filelib#wildcard/1">
+ <c>filelib:wildcard/1</c></seemfa>.
If a regular file is found among the matches, the function
returns that location together with <c><anno>Options</anno></c>.
Otherwise the next rule is tried, and so on.</p>
@@ -494,7 +494,7 @@ true
shell and native applications on the current platform. On Windows,
forward slashes are converted to backward slashes. On all
platforms, the name is normalized as done by
- <seealso marker="#join/1"><c>join/1</c></seealso>.</p>
+ <seemfa marker="#join/1"><c>join/1</c></seemfa>.</p>
<p><em>Examples:</em></p>
<pre>
19> <input>filename:nativename("/usr/local/bin/").</input> % Unix
@@ -570,6 +570,10 @@ true
<item><p>A ".." component would climb up above the root of
the relative path.</p></item>
</list>
+ <warning>
+ <p>This function is deprecated. Use <seemfa marker="filelib#safe_relative_path/2">
+ <c>filelib:safe_relative_path/2</c></seemfa> instead for sanitizing paths.</p>
+ </warning>
<p><em>Examples:</em></p>
<pre>
1> <input>filename:safe_relative_path("dir/sub_dir/..").</input>
diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml
index a9596c6e4d..ffdcf54c5a 100644
--- a/lib/stdlib/doc/src/gb_sets.xml
+++ b/lib/stdlib/doc/src/gb_sets.xml
@@ -62,44 +62,44 @@
<title>Compatibility</title>
<p>The following functions in this module also exist and provides
the same functionality in the
- <seealso marker="sets"><c>sets(3)</c></seealso> and
- <seealso marker="ordsets"><c>ordsets(3)</c></seealso>
+ <seeerl marker="sets"><c>sets(3)</c></seeerl> and
+ <seeerl marker="ordsets"><c>ordsets(3)</c></seeerl>
modules. That is, by only changing the module name for each call,
you can try out different set representations.</p>
<list type="bulleted">
- <item><seealso marker="#add_element/2"><c>add_element/2</c></seealso>
+ <item><seemfa marker="#add_element/2"><c>add_element/2</c></seemfa>
</item>
- <item><seealso marker="#del_element/2"><c>del_element/2</c></seealso>
+ <item><seemfa marker="#del_element/2"><c>del_element/2</c></seemfa>
</item>
- <item><seealso marker="#filter/2"><c>filter/2</c></seealso>
+ <item><seemfa marker="#filter/2"><c>filter/2</c></seemfa>
</item>
- <item><seealso marker="#fold/3"><c>fold/3</c></seealso>
+ <item><seemfa marker="#fold/3"><c>fold/3</c></seemfa>
</item>
- <item><seealso marker="#from_list/1"><c>from_list/1</c></seealso>
+ <item><seemfa marker="#from_list/1"><c>from_list/1</c></seemfa>
</item>
- <item><seealso marker="#intersection/1"><c>intersection/1</c></seealso>
+ <item><seemfa marker="#intersection/1"><c>intersection/1</c></seemfa>
</item>
- <item><seealso marker="#intersection/2"><c>intersection/2</c></seealso>
+ <item><seemfa marker="#intersection/2"><c>intersection/2</c></seemfa>
</item>
- <item><seealso marker="#is_element/2"><c>is_element/2</c></seealso>
+ <item><seemfa marker="#is_element/2"><c>is_element/2</c></seemfa>
</item>
- <item><seealso marker="#is_empty/1"><c>is_empty/1</c></seealso>
+ <item><seemfa marker="#is_empty/1"><c>is_empty/1</c></seemfa>
</item>
- <item><seealso marker="#is_set/1"><c>is_set/1</c></seealso>
+ <item><seemfa marker="#is_set/1"><c>is_set/1</c></seemfa>
</item>
- <item><seealso marker="#is_subset/2"><c>is_subset/2</c></seealso>
+ <item><seemfa marker="#is_subset/2"><c>is_subset/2</c></seemfa>
</item>
- <item><seealso marker="#new/0"><c>new/0</c></seealso>
+ <item><seemfa marker="#new/0"><c>new/0</c></seemfa>
</item>
- <item><seealso marker="#size/1"><c>size/1</c></seealso>
+ <item><seemfa marker="#size/1"><c>size/1</c></seemfa>
</item>
- <item><seealso marker="#subtract/2"><c>subtract/2</c></seealso>
+ <item><seemfa marker="#subtract/2"><c>subtract/2</c></seemfa>
</item>
- <item><seealso marker="#to_list/1"><c>to_list/1</c></seealso>
+ <item><seemfa marker="#to_list/1"><c>to_list/1</c></seemfa>
</item>
- <item><seealso marker="#union/1"><c>union/1</c></seealso>
+ <item><seemfa marker="#union/1"><c>union/1</c></seemfa>
</item>
- <item><seealso marker="#union/2"><c>union/2</c></seealso>
+ <item><seemfa marker="#union/2"><c>union/2</c></seemfa>
</item>
</list>
</section>
@@ -324,10 +324,10 @@
<desc>
<p>Returns an iterator that can be used for traversing the entries of
<c><anno>Set</anno></c>; see
- <seealso marker="#next/1"><c>next/1</c></seealso>. The implementation
+ <seemfa marker="#next/1"><c>next/1</c></seemfa>. 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 <seealso marker="#to_list/1"><c>to_list/1</c></seealso>
+ elements using <seemfa marker="#to_list/1"><c>to_list/1</c></seemfa>
and traversing that.
The main advantage of the iterator approach is that it does
not require the complete list of all elements to be built in
@@ -342,9 +342,9 @@
<desc>
<p>Returns an iterator that can be used for traversing the
entries of <c><anno>Set</anno></c>; see
- <seealso marker="#next/1"><c>next/1</c></seealso>.
+ <seemfa marker="#next/1"><c>next/1</c></seemfa>.
The difference as compared to the iterator returned by
- <seealso marker="#iterator/1"><c>iterator/1</c></seealso>
+ <seemfa marker="#iterator/1"><c>iterator/1</c></seemfa>
is that the first element greater than
or equal to <c><anno>Element</anno></c> is returned.</p>
</desc>
@@ -467,9 +467,9 @@
<section>
<title>See Also</title>
- <p><seealso marker="gb_trees"><c>gb_trees(3)</c></seealso>,
- <seealso marker="ordsets"><c>ordsets(3)</c></seealso>,
- <seealso marker="sets"><c>sets(3)</c></seealso></p>
+ <p><seeerl marker="gb_trees"><c>gb_trees(3)</c></seeerl>,
+ <seeerl marker="ordsets"><c>ordsets(3)</c></seeerl>,
+ <seeerl marker="sets"><c>sets(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml
index 9a9f61ae52..c8944347a5 100644
--- a/lib/stdlib/doc/src/gb_trees.xml
+++ b/lib/stdlib/doc/src/gb_trees.xml
@@ -202,11 +202,11 @@
<desc>
<p>Returns an iterator that can be used for traversing the
entries of <c><anno>Tree</anno></c>; see
- <seealso marker="#next/1"><c>next/1</c></seealso>. The implementation
+ <seemfa marker="#next/1"><c>next/1</c></seemfa>. 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
- <seealso marker="#to_list/1"><c>to_list/1</c></seealso>
+ <seemfa marker="#to_list/1"><c>to_list/1</c></seemfa>
and traversing that.
The main advantage of the iterator approach is that it does
not require the complete list of all elements to be built in
@@ -221,9 +221,9 @@
<desc>
<p>Returns an iterator that can be used for traversing the
entries of <c><anno>Tree</anno></c>; see
- <seealso marker="#next/1"><c>next/1</c></seealso>.
+ <seemfa marker="#next/1"><c>next/1</c></seemfa>.
The difference as compared to the iterator returned by
- <seealso marker="#iterator/1"><c>iterator/1</c></seealso>
+ <seemfa marker="#iterator/1"><c>iterator/1</c></seemfa>
is that the first key greater than
or equal to <c><anno>Key</anno></c> is returned.</p>
</desc>
@@ -360,8 +360,8 @@
<section>
<title>See Also</title>
- <p><seealso marker="dict"><c>dict(3)</c></seealso>,
- <seealso marker="gb_sets"><c>gb_sets(3)</c></seealso></p>
+ <p><seeerl marker="dict"><c>dict(3)</c></seeerl>,
+ <seeerl marker="gb_sets"><c>gb_sets(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index 2915c4f507..2ddff240d2 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -39,7 +39,7 @@
set of interface functions and includes functionality for tracing and
error reporting. It also fits into an OTP supervision tree. For more
information, see
- <seealso marker="doc/design_principles:events">OTP Design Principles</seealso>.
+ <seeguide marker="system/design_principles:events">OTP Design Principles</seeguide>.
</p>
<p>Each event handler is implemented as a callback module exporting
@@ -50,6 +50,7 @@
gen_event module Callback module
---------------- ---------------
gen_event:start
+gen_event:start_monitor
gen_event:start_link -----> -
gen_event:add_handler
@@ -58,6 +59,7 @@ gen_event:add_sup_handler -----> Module:init/1
gen_event:notify
gen_event:sync_notify -----> Module:handle_event/2
+gen_event:send_request
gen_event:call -----> Module:handle_call/2
- -----> Module:handle_info/2
@@ -81,21 +83,21 @@ gen_event:stop -----> Module:terminate/2
an installed event handler fails with <c>Reason</c>, or returns a
bad value <c>Term</c>, the event manager does not fail. It deletes
the event handler by calling callback function
- <seealso marker="#Module:terminate/2"><c>Module:terminate/2</c></seealso>,
+ <seemfa marker="#Module:terminate/2"><c>Module:terminate/2</c></seemfa>,
giving as argument
<c>{error,{'EXIT',Reason}}</c> or <c>{error,Term}</c>, respectively.
No other event handler is affected.</p>
<p>A <c>gen_event</c> process handles system messages as described in
- <seealso marker="sys"><c>sys(3)</c></seealso>. The <c>sys</c> module
+ <seeerl marker="sys"><c>sys(3)</c></seeerl>. The <c>sys</c> module
can be used for debugging an event manager.</p>
<p>Notice that an event manager <em>does</em> trap exit signals
automatically.</p>
<p>The <c>gen_event</c> process can go into hibernation
- (see <seealso marker="erts:erlang#hibernate/3">
- <c>erlang:hibernate/3</c></seealso>) if a callback function in
+ (see <seemfa marker="erts:erlang#hibernate/3">
+ <c>erlang:hibernate/3</c></seemfa>) if a callback function in
a handler module specifies <c>hibernate</c> in its return value.
This can be useful if the server is expected to be idle for a long
time. However, use this feature with care, as hibernation
@@ -126,6 +128,15 @@ gen_event:stop -----> Module:terminate/2
<datatype>
<name name="del_handler_ret"/>
</datatype>
+ <datatype>
+ <name name="request_id"/>
+ <desc>
+ <p>
+ A request handle, see <seemfa marker="#send_request/3"> <c>send_request/3</c> </seemfa>
+ for details.
+ </p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
@@ -147,7 +158,7 @@ gen_event:stop -----> Module:terminate/2
<desc>
<p>Adds a new event handler to event manager <c>EventMgrRef</c>.
The event manager calls
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
to initiate the event handler and its internal state.</p>
<p><c>EventMgrRef</c> can be any of the following:</p>
<list type="bulleted">
@@ -195,14 +206,14 @@ gen_event:stop -----> Module:terminate/2
</type>
<desc>
<p>Adds a new event handler in the same way as
- <seealso marker="#add_handler/3"><c>add_handler/3</c></seealso>,
+ <seemfa marker="#add_handler/3"><c>add_handler/3</c></seemfa>,
but also supervises the connection between the event handler
and the calling process.</p>
<list type="bulleted">
<item>If the calling process later terminates with <c>Reason</c>,
the event manager deletes the event handler by calling
- <seealso marker="#Module:terminate/2">
- <c>Module:terminate/2</c></seealso>
+ <seemfa marker="#Module:terminate/2">
+ <c>Module:terminate/2</c></seemfa>
with <c>{stop,Reason}</c> as argument.
</item>
<item>
@@ -224,10 +235,10 @@ gen_event:stop -----> Module:terminate/2
<p><c>{swapped,NewHandler,Pid}</c>, if the process <c>Pid</c>
has replaced the event handler with another event handler
<c>NewHandler</c> using a call to
- <seealso marker="#swap_handler/3">
- <c>swap_handler/3</c></seealso> or
- <seealso marker="#swap_sup_handler/3">
- <c>swap_sup_handler/3</c></seealso>.</p>
+ <seemfa marker="#swap_handler/3">
+ <c>swap_handler/3</c></seemfa> or
+ <seemfa marker="#swap_sup_handler/3">
+ <c>swap_sup_handler/3</c></seemfa>.</p>
</item>
<item>
<p>A term, if the event handler is removed because of an error.
@@ -236,7 +247,7 @@ gen_event:stop -----> Module:terminate/2
</item>
</list>
<p>For a description of the arguments and return values, see
- <seealso marker="#add_handler/3"><c>add_handler/3</c></seealso>.</p>
+ <seemfa marker="#add_handler/3"><c>add_handler/3</c></seemfa>.</p>
</desc>
</func>
@@ -263,10 +274,10 @@ gen_event:stop -----> Module:terminate/2
<p>Makes a synchronous call to event handler <c>Handler</c>
installed in event manager <c>EventMgrRef</c> by sending a
request and waiting until a reply arrives or a time-out occurs.
- The event manager calls <seealso marker="#Module:handle_call/2">
- <c>Module:handle_call/2</c></seealso> to handle the request.</p>
+ The event manager calls <seemfa marker="#Module:handle_call/2">
+ <c>Module:handle_call/2</c></seemfa> to handle the request.</p>
<p>For a description of <c>EventMgrRef</c> and <c>Handler</c>, see
- <seealso marker="#add_handler/3"><c>add_handler/3</c></seealso>.</p>
+ <seemfa marker="#add_handler/3"><c>add_handler/3</c></seemfa>.</p>
<p><c>Request</c> is any term that is passed as one of
the arguments to <c>Module:handle_call/2</c>.</p>
<p><c>Timeout</c> is an integer greater than zero that specifies
@@ -284,6 +295,40 @@ gen_event:stop -----> Module:terminate/2
</desc>
</func>
+
+ <func>
+ <name since="OTP-23">check_response(Msg, RequestId) -> Result</name>
+ <fsummary>Check if a message is a reply from a server.</fsummary>
+ <type>
+ <v>Msg = term()</v>
+ <v>RequestId = request_id()</v>
+ <v>Result = {reply, Reply} | no_reply | {error, Error}</v>
+ <v>Reply = Error = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is used to check if a previously received
+ message, for example by <c>receive</c> or
+ <c>handle_info/2</c>, is a result of a request made with
+ <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>.
+ If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+ the result of the request is returned in <c>Reply</c>.
+ Otherwise returns <c>no_reply</c> and no cleanup is done, and
+ thus the function shall be invoked repeatedly until a reply
+ is returned.
+ </p>
+ <p>
+ If the specified event handler is not
+ installed, the function returns <c>{error,bad_module}</c>. If
+ the callback function fails with <c>Reason</c> or returns an
+ unexpected value <c>Term</c>, this function returns
+ <c>{error,{'EXIT',Reason}}</c> or <c>{error,Term}</c>,
+ respectively. If the event manager dies before or during the
+ request this function returns <c>{error,{Reason, EventMgrRef}}</c>.
+ </p>
+ </desc>
+ </func>
+
<func>
<name since="">delete_handler(EventMgrRef, Handler, Args) -> Result</name>
<fsummary>Delete an event handler from a generic event manager.</fsummary>
@@ -302,11 +347,11 @@ gen_event:stop -----> Module:terminate/2
<desc>
<p>Deletes an event handler from event manager
<c>EventMgrRef</c>. The event manager calls
- <seealso marker="#Module:terminate/2">
- <c>Module:terminate/2</c></seealso> to terminate the event
+ <seemfa marker="#Module:terminate/2">
+ <c>Module:terminate/2</c></seemfa> to terminate the event
handler.</p>
<p>For a description of <c>EventMgrRef</c> and <c>Handler</c>, see
- <seealso marker="#add_handler/3"><c>add_handler/3</c></seealso>.</p>
+ <seemfa marker="#add_handler/3"><c>add_handler/3</c></seemfa>.</p>
<p><c>Args</c> is any term that is passed as one of
the arguments to <c>Module:terminate/2</c>.</p>
<p>The return value is the return value of <c>Module:terminate/2</c>.
@@ -331,23 +376,66 @@ gen_event:stop -----> Module:terminate/2
<desc>
<p>Sends an event notification to event manager
<c>EventMgrRef</c>. The event manager calls
- <seealso marker="#Module:handle_event/2">
- <c>Module:handle_event/2</c></seealso>
+ <seemfa marker="#Module:handle_event/2">
+ <c>Module:handle_event/2</c></seemfa>
for each installed event handler to handle the event.</p>
<p><c>notify/2</c> is asynchronous and returns immediately after
the event notification has been sent. <c>sync_notify/2</c> is
synchronous in the sense that it returns <c>ok</c> after
the event has been handled by all event handlers.</p>
<p>For a description of <c>EventMgrRef</c>, see
- <seealso marker="#add_handler/3"><c>add_handler/3</c></seealso>.</p>
+ <seemfa marker="#add_handler/3"><c>add_handler/3</c></seemfa>.</p>
<p><c>Event</c> is any term that is passed as one of
- the arguments to <seealso marker="#Module:handle_event/2">
- <c>Module:handle_event/2</c></seealso>.</p>
+ the arguments to <seemfa marker="#Module:handle_event/2">
+ <c>Module:handle_event/2</c></seemfa>.</p>
<p><c>notify/1</c> does not fail even if the specified event manager
does not exist, unless it is specified as <c>Name</c>.</p>
</desc>
</func>
+
+ <func>
+ <name since="OTP-23">send_request(EventMgrRef, Handler, Request) -> RequestId</name>
+ <fsummary>Send a request to a generic event manager.</fsummary>
+ <type>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Handler = Module | {Module,Id}</v>
+ <v>&nbsp;Module = atom()</v>
+ <v>&nbsp;Id = term()</v>
+ <v>Request = term()</v>
+ <v>RequestId = request_id()</v>
+ </type>
+ <desc>
+ <p>
+ Sends a request to event handler <c>Handler</c> installed in
+ event manager <c>EventMgrRef</c> and returns a handle
+ <c>RequestId</c>. The return value <c>RequestId</c> shall
+ later be used with <seemfa marker="#wait_response/2">
+ <c>wait_response/2</c></seemfa> or <seemfa
+ marker="#check_response/2">
+ <c>check_response/2</c></seemfa> in the same process to
+ fetch the actual result of the request.
+ </p>
+ <p>
+ The call <c>gen_event:wait_response(gen_event:send_request(EventMgrRef,Handler,Request), Timeout)</c>
+ can be seen as equivalent to
+ <seemfa marker="#call/3"><c>gen_event:call(EventMgrRef,Handler,Request,Timeout)</c></seemfa>,
+ ignoring the error handling.
+ </p>
+ <p>
+ The event manager calls <seemfa marker="#Module:handle_call/2">
+ <c>Module:handle_call/2</c></seemfa> to handle the request.
+ </p>
+ <p>
+ <c>Request</c> is any term that is passed as one of
+ the arguments to <c>Module:handle_call/3</c>.
+ </p>
+ </desc>
+ </func>
+
<func>
<name since="">start() -> Result</name>
<name since="">start(EventMgrName | Options) -> Result</name>
@@ -370,7 +458,7 @@ gen_event:stop -----> Module:terminate/2
manager that is not part of a supervision tree and thus has
no supervisor.</p>
<p>For a description of the arguments and return values, see
- <seealso marker="#start_link/0"><c>start_link/0,1</c></seealso>.</p>
+ <seemfa marker="#start_link/0"><c>start_link/0,1</c></seemfa>.</p>
</desc>
</func>
@@ -405,8 +493,8 @@ gen_event:stop -----> Module:terminate/2
<item>
<p>If <c>EventMgrName={global,GlobalName}</c>, the event manager is
registered globally as <c>GlobalName</c> using
- <seealso marker="kernel:global#register_name/2">
- <c>global:register_name/2</c></seealso>.
+ <seemfa marker="kernel:global#register_name/2">
+ <c>global:register_name/2</c></seemfa>.
If no name is provided, the event manager is not registered.</p>
</item>
<item>
@@ -416,14 +504,14 @@ gen_event:stop -----> Module:terminate/2
<c>register_name/2</c>, <c>unregister_name/1</c>,
<c>whereis_name/1</c>, and <c>send/2</c>, which are to behave
as the corresponding functions in
- <seealso marker="kernel:global"><c>global</c></seealso>.
+ <seeerl marker="kernel:global"><c>global</c></seeerl>.
Thus, <c>{via,global,GlobalName}</c> is a valid reference.</p>
</item>
<item>
<p>If option <c>{hibernate_after,HibernateAfterTimeout}</c> is present, the <c>gen_event</c>
process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and
if no message is received, the process goes into hibernation automatically
- (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).</p>
+ (by calling <seemfa marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seemfa>).</p>
</item>
</list>
<p>If the event manager is successfully created, the function
@@ -436,6 +524,40 @@ gen_event:stop -----> Module:terminate/2
</func>
<func>
+ <name since="OTP 23.0">start_monitor() -> Result</name>
+ <name since="OTP 23.0">start_monitor(EventMgrName | Options) -> Result</name>
+ <name since="OTP 23.0">start_monitor(EventMgrName, Options) -> Result</name>
+ <fsummary>Create a stand-alone event manager process.</fsummary>
+ <type>
+ <v>EventMgrName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}</v>
+ <v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Options = [Option]</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
+ <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
+ <v>&nbsp;&nbsp;SOpts = [term()]</v>
+ <v>Result = {ok,{Pid,Mon}} | {error,{already_started,Pid}}</v>
+ <v>&nbsp;Pid = pid()</v>
+ </type>
+ <desc>
+ <p>Creates a stand-alone event manager process, that is, an event
+ manager that is not part of a supervision tree (and thus has
+ no supervisor) and atomically sets up a monitor to
+ the newly created process.</p>
+ <p>For a description of the arguments and return values, see
+ <seemfa marker="#start_link/0"><c>start_link/0,1</c></seemfa>.
+ Note that the return value on successful start differs from
+ <c>start_link/3,4</c>. <c>start_monitor/3,4</c> will return
+ <c>{ok,{Pid,Mon}}</c> where <c>Pid</c> is the process identifier
+ of the process, and <c>Mon</c> is a reference to the monitor
+ set up to monitor the process. If the start is not successful,
+ the caller will be blocked until the <c>DOWN</c> message has
+ been received and removed from the message queue.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="">stop(EventMgrRef) -> ok</name>
<name since="OTP 18.0">stop(EventMgrRef, Reason, Timeout) -> ok</name>
<fsummary>Terminate a generic event manager.</fsummary>
@@ -451,14 +573,14 @@ gen_event:stop -----> Module:terminate/2
<p>Orders event manager <c>EventMgrRef</c> to exit with
the specifies <c>Reason</c> and waits for it to
terminate. Before terminating, <c>gen_event</c> calls
- <seealso marker="#Module:terminate/2">
- <c>Module:terminate(stop,...)</c></seealso>
+ <seemfa marker="#Module:terminate/2">
+ <c>Module:terminate(stop,...)</c></seemfa>
for each installed event handler.</p>
<p>The function returns <c>ok</c> if the event manager terminates
with the expected reason. Any other reason than <c>normal</c>,
<c>shutdown</c>, or <c>{shutdown,Term}</c> causes an
error report to be issued using
- <seealso marker="kernel:logger"><c>logger(3)</c></seealso>.
+ <seeerl marker="kernel:logger"><c>logger(3)</c></seeerl>.
The default <c>Reason</c> is <c>normal</c>.</p>
<p><c>Timeout</c> is an integer greater than zero that
specifies how many milliseconds to wait for the event manager to
@@ -469,7 +591,7 @@ gen_event:stop -----> Module:terminate/2
<p>If the process does not exist, a <c>noproc</c> exception
is raised.</p>
<p>For a description of <c>EventMgrRef</c>, see
- <seealso marker="#add_handler/3"><c>add_handler/3</c></seealso>.</p>
+ <seemfa marker="#add_handler/3"><c>add_handler/3</c></seemfa>.</p>
</desc>
</func>
@@ -493,7 +615,7 @@ gen_event:stop -----> Module:terminate/2
<p>Replaces an old event handler with a new event handler in
event manager <c>EventMgrRef</c>.</p>
<p>For a description of the arguments, see
- <seealso marker="#add_handler/3"><c>add_handler/3</c></seealso>.</p>
+ <seemfa marker="#add_handler/3"><c>add_handler/3</c></seemfa>.</p>
<p>First the old event handler <c>Handler1</c> is deleted.
The event manager calls <c>Module1:terminate(Args1, ...)</c>,
where <c>Module1</c> is the callback module of <c>Handler1</c>,
@@ -541,7 +663,50 @@ gen_event:stop -----> Module:terminate/2
in the same way as <c>swap_handler/3</c>, but also supervises
the connection between <c>Handler2</c> and the calling process.</p>
<p>For a description of the arguments and return values, see
- <seealso marker="#swap_handler/3"><c>swap_handler/3</c></seealso>.</p>
+ <seemfa marker="#swap_handler/3"><c>swap_handler/3</c></seemfa>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name since="OTP-23">wait_response(RequestId, Timeout) -> Result</name>
+ <fsummary>Wait for a reply from a server.</fsummary>
+ <type>
+ <v>RequestId = request_id()</v>
+ <v>Reply = term()</v>
+ <v>Timeout = timeout()</v>
+ <v>Result = {reply, Reply} | timeout | {error, Error}</v>
+ <v>Reply = Error = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is used to wait for a reply of a request made with
+ <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
+ from the event manager. This function must be called from the same
+ process from which <seemfa marker="#send_request/3"><c>send_request/3</c></seemfa>
+ was made.
+ </p>
+ <p>
+ <c>Timeout</c> is an integer greater then or equal to zero
+ that specifies how many milliseconds to wait for an reply, or
+ the atom <c>infinity</c> to wait indefinitely.
+ If no reply is received within the specified
+ time, the function returns <c>timeout</c> and no cleanup is
+ done, and thus the function must be invoked repeatedly until a
+ reply is returned.
+ </p>
+ <p>
+ The return value <c>Reply</c> is defined in the return value
+ of <c>Module:handle_call/3</c>.
+ </p>
+ <p>
+ If the specified event handler is not
+ installed, the function returns <c>{error,bad_module}</c>. If
+ the callback function fails with <c>Reason</c> or returns an
+ unexpected value <c>Term</c>, this function returns
+ <c>{error,{'EXIT',Reason}}</c> or <c>{error,Term}</c>,
+ respectively. If the event manager dies before or during the
+ request this function returns <c>{error,{Reason, EventMgrRef}}</c>.
+ </p>
</desc>
</func>
@@ -562,18 +727,19 @@ gen_event:stop -----> Module:terminate/2
<p>Returns a list of all event handlers installed in event
manager <c>EventMgrRef</c>.</p>
<p>For a description of <c>EventMgrRef</c> and <c>Handler</c>, see
- <seealso marker="#add_handler/3"><c>add_handler/3</c></seealso>.</p>
+ <seemfa marker="#add_handler/3"><c>add_handler/3</c></seemfa>.</p>
</desc>
</func>
</funcs>
- <section>
- <title>Callback Functions</title>
- <p>The following functions are to be exported from a <c>gen_event</c>
- callback module.</p>
- </section>
+
<funcs>
+ <fsdescription>
+ <title>Callback Functions</title>
+ <p>The following functions are to be exported from a <c>gen_event</c>
+ callback module.</p>
+ </fsdescription>
<func>
<name since="">Module:code_change(OldVsn, State, Extra) -> {ok, NewState}</name>
<fsummary>Update the internal state during upgrade/downgrade.</fsummary>
@@ -596,8 +762,8 @@ gen_event:stop -----> Module:terminate/2
upgrade/downgrade, that is, when the instruction
<c>{update,Module,Change,...}</c>, where
<c>Change={advanced,Extra}</c>, is specified in the <c>.appup</c>
- file. For more information, see <seealso
- marker="doc/design_principles:users_guide">OTP Design Principles</seealso>.</p>
+ file. For more information, see <seeguide
+ marker="system/design_principles:index">OTP Design Principles</seeguide>.</p>
<p>For an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and for a downgrade,
<c>OldVsn</c> is <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the
<c>vsn</c> attribute(s) of the old version of the callback module
@@ -630,8 +796,8 @@ gen_event:stop -----> Module:terminate/2
<p>This function is called by a <c>gen_event</c> process in the
following situations:</p>
<list type="bulleted">
- <item>One of <seealso marker="sys#get_status/1">
- <c>sys:get_status/1,2</c></seealso>
+ <item>One of <seemfa marker="sys#get_status/1">
+ <c>sys:get_status/1,2</c></seemfa>
is invoked to get the <c>gen_event</c> status. <c>Opt</c> is set
to the atom <c>normal</c> for this case.</item>
<item>The event handler terminates abnormally and <c>gen_event</c>
@@ -684,14 +850,14 @@ gen_event:stop -----> Module:terminate/2
</type>
<desc>
<p>Whenever an event manager receives a request sent using
- <seealso marker="#call/3"><c>call/3,4</c></seealso>,
+ <seemfa marker="#call/3"><c>call/3,4</c></seemfa>,
this function is called for
the specified event handler to handle the request.</p>
<p><c>Request</c> is the <c>Request</c> argument of <c>call/3,4</c>.</p>
<p><c>State</c> is the internal state of the event handler.</p>
<p>The return values are the same as for
- <seealso marker="#Module:handle_event/2">
- <c>Module:handle_event/2</c></seealso>
+ <seemfa marker="#Module:handle_event/2">
+ <c>Module:handle_event/2</c></seemfa>
except that they also contain a term <c>Reply</c>, which is the reply
to the client as the return value of <c>call/3,4</c>.</p>
</desc>
@@ -714,8 +880,8 @@ gen_event:stop -----> Module:terminate/2
</type>
<desc>
<p>Whenever an event manager receives an event sent using
- <seealso marker="#notify/2"><c>notify/2</c></seealso> or
- <seealso marker="#sync_notify/2"><c>sync_notify/2</c></seealso>,
+ <seemfa marker="#notify/2"><c>notify/2</c></seemfa> or
+ <seemfa marker="#sync_notify/2"><c>sync_notify/2</c></seemfa>,
this function is called for each installed event handler to handle
the event.</p>
<p><c>Event</c> is the <c>Event</c> argument of
@@ -731,8 +897,8 @@ gen_event:stop -----> Module:terminate/2
<item>
<p>If <c>{ok,NewState,hibernate}</c> is returned, the event
manager also goes into hibernation (by calling
- <seealso marker="proc_lib#hibernate/3">
- <c>proc_lib:hibernate/3</c></seealso>), waiting for the next
+ <seemfa marker="proc_lib#hibernate/3">
+ <c>proc_lib:hibernate/3</c></seemfa>), waiting for the next
event to occur. It is sufficient that one of the
event handlers return <c>{ok,NewState,hibernate}</c> for the
whole event manager process to hibernate.</p>
@@ -743,7 +909,7 @@ gen_event:stop -----> Module:terminate/2
first calling <c>Module:terminate(Args1,NewState)</c> and then
<c>Module2:init({Args2,Term})</c>, where <c>Term</c> is the return
value of <c>Module:terminate/2</c>. For more information, see
- <seealso marker="#swap_handler/3"><c>swap_handler/3</c></seealso>.
+ <seemfa marker="#swap_handler/3"><c>swap_handler/3</c></seemfa>.
</p>
</item>
<item>
@@ -782,8 +948,8 @@ gen_event:stop -----> Module:terminate/2
a synchronous request (or a system message).</p>
<p><c>Info</c> is the received message.</p>
<p>For a description of <c>State</c> and possible return values, see
- <seealso marker="#Module:handle_event/2">
- <c>Module:handle_event/2</c></seealso>.</p>
+ <seemfa marker="#Module:handle_event/2">
+ <c>Module:handle_event/2</c></seemfa>.</p>
</desc>
</func>
@@ -800,26 +966,26 @@ gen_event:stop -----> Module:terminate/2
<p>Whenever a new event handler is added to an event manager,
this function is called to initialize the event handler.</p>
<p>If the event handler is added because of a call to
- <seealso marker="#add_handler/3"><c>add_handler/3</c></seealso> or
- <seealso marker="#add_sup_handler/3">
- <c>add_sup_handler/3</c></seealso>, <c>InitArgs</c> is
+ <seemfa marker="#add_handler/3"><c>add_handler/3</c></seemfa> or
+ <seemfa marker="#add_sup_handler/3">
+ <c>add_sup_handler/3</c></seemfa>, <c>InitArgs</c> is
the <c>Args</c> argument of these functions.</p>
<p>If the event handler replaces another event handler because of
a call to
- <seealso marker="#swap_handler/3"><c>swap_handler/3</c></seealso> or
- <seealso marker="#swap_sup_handler/3">
- <c>swap_sup_handler/3</c></seealso>, or because of a <c>swap</c>
+ <seemfa marker="#swap_handler/3"><c>swap_handler/3</c></seemfa> or
+ <seemfa marker="#swap_sup_handler/3">
+ <c>swap_sup_handler/3</c></seemfa>, or because of a <c>swap</c>
return tuple from one of the other callback functions,
<c>InitArgs</c> is a tuple <c>{Args,Term}</c>, where <c>Args</c> is
the argument provided in the function call/return tuple and
<c>Term</c> is the result of terminating the old event handler, see
- <seealso marker="#swap_handler/3"><c>swap_handler/3</c></seealso>.</p>
+ <seemfa marker="#swap_handler/3"><c>swap_handler/3</c></seemfa>.</p>
<p>If successful, the function returns <c>{ok,State}</c>
or <c>{ok,State,hibernate}</c>, where <c>State</c> is the
initial internal state of the event handler.</p>
<p>If <c>{ok,State,hibernate}</c> is returned, the event
- manager goes into hibernation (by calling <seealso
- marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>),
+ manager goes into hibernation (by calling <seemfa
+ marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seemfa>),
waiting for the next event to occur.</p>
</desc>
</func>
@@ -840,13 +1006,13 @@ gen_event:stop -----> Module:terminate/2
</note>
<p>Whenever an event handler is deleted from an event manager,
this function is called. It is to be the opposite of
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
and do any necessary cleaning up.</p>
<p>If the event handler is deleted because of a call to
- <seealso marker="#delete_handler/3"><c>delete_handler/3</c></seealso>,
- <seealso marker="#swap_handler/3"><c>swap_handler/3</c></seealso>, or
- <seealso marker="#swap_sup_handler/3">
- <c>swap_sup_handler/3</c></seealso>, <c>Arg</c> is
+ <seemfa marker="#delete_handler/3"><c>delete_handler/3</c></seemfa>,
+ <seemfa marker="#swap_handler/3"><c>swap_handler/3</c></seemfa>, or
+ <seemfa marker="#swap_sup_handler/3">
+ <c>swap_sup_handler/3</c></seemfa>, <c>Arg</c> is
the <c>Args</c> argument of this function call.</p>
<p><c>Arg={stop,Reason}</c> if the event handler has a supervised
connection to a process that has terminated with reason
@@ -879,7 +1045,7 @@ gen_event:stop -----> Module:terminate/2
<section>
<title>See Also</title>
- <p><seealso marker="supervisor"><c>supervisor(3)</c></seealso>,
- <seealso marker="sys"><c>sys(3)</c></seealso></p>
+ <p><seeerl marker="supervisor"><c>supervisor(3)</c></seeerl>,
+ <seeerl marker="sys"><c>sys(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml
index 7187630b43..03fce5aae4 100644
--- a/lib/stdlib/doc/src/gen_fsm.xml
+++ b/lib/stdlib/doc/src/gen_fsm.xml
@@ -32,14 +32,14 @@
<modulesummary>Deprecated and replaced by gen_statem </modulesummary>
<description>
- <p> Deprecated and replaced by <seealso marker="gen_statem"><c>gen_statem</c></seealso> </p>
+ <p> Deprecated and replaced by <seeerl marker="gen_statem"><c>gen_statem</c></seeerl> </p>
</description>
<section>
<marker id="Migration to gen_statem"/>
<title>Migration to gen_statem</title>
<p>Here follows a simple example of turning a gen_fsm into
- a <seealso marker="gen_statem"><c>gen_statem</c></seealso>. The example comes
+ a <seeerl marker="gen_statem"><c>gen_statem</c></seeerl>. The example comes
from the previous Users Guide for <c>gen_fsm</c> </p>
<code type="erl">
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index a4554d7657..4abb91439e 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -36,8 +36,8 @@
this module has a standard set of interface functions and
includes functionality for tracing and error reporting. It also
fits into an OTP supervision tree. For more information, see section
- <seealso marker="doc/design_principles:gen_server_concepts">
- gen_server Behaviour</seealso> in OTP Design Principles.</p>
+ <seeguide marker="system/design_principles:gen_server_concepts">
+ gen_server Behaviour</seeguide> in OTP Design Principles.</p>
<p>A <c>gen_server</c> process assumes all specific parts to be located in
a callback module exporting a predefined set of functions.
@@ -48,11 +48,13 @@
gen_server module Callback module
----------------- ---------------
gen_server:start
+gen_server:start_monitor
gen_server:start_link -----> Module:init/1
gen_server:stop -----> Module:terminate/2
gen_server:call
+gen_server:send_request
gen_server:multi_call -----> Module:handle_call/3
gen_server:cast
@@ -70,7 +72,7 @@ gen_server:abcast -----> Module:handle_cast/2
<c>gen_server</c> process terminates.</p>
<p>A <c>gen_server</c> process handles system messages as described in
- <seealso marker="sys"><c>sys(3)</c></seealso>. The <c>sys</c> module
+ <seeerl marker="sys"><c>sys(3)</c></seeerl>. The <c>sys</c> module
can be used for debugging a <c>gen_server</c> process.</p>
<p>Notice that a <c>gen_server</c> process does not trap exit signals
@@ -82,8 +84,8 @@ gen_server:abcast -----> Module:handle_cast/2
arguments are specified.</p>
<p>The <c>gen_server</c> process can go into hibernation
- (see <seealso marker="erts:erlang#hibernate/3">
- <c>erlang:hibernate/3</c></seealso>) if a callback
+ (see <seemfa marker="erts:erlang#hibernate/3">
+ <c>erlang:hibernate/3</c></seemfa>) if a callback
function specifies <c>'hibernate'</c> instead of a time-out value. This
can be useful if the server is expected to be idle for a long
time. However, use this feature with care, as hibernation
@@ -116,10 +118,10 @@ gen_server:abcast -----> Module:handle_cast/2
returns immediately and ignores nodes that do not exist, or
where the <c>gen_server</c> <c>Name</c> does not exist.
The <c>gen_server</c> processes call
- <seealso marker="#Module:handle_cast/2">
- <c>Module:handle_cast/2</c></seealso> to handle the request.</p>
+ <seemfa marker="#Module:handle_cast/2">
+ <c>Module:handle_cast/2</c></seemfa> to handle the request.</p>
<p>For a description of the arguments, see
- <seealso marker="#multi_call/2"><c>multi_call/2,3,4</c></seealso>.</p>
+ <seemfa marker="#multi_call/2"><c>multi_call/2,3,4</c></seemfa>.</p>
</desc>
</func>
@@ -141,8 +143,8 @@ gen_server:abcast -----> Module:handle_cast/2
<c>gen_server</c> process
by sending a request and waiting until a reply arrives or a
time-out occurs. The <c>gen_server</c> process calls
- <seealso marker="#Module:handle_call/3">
- <c>Module:handle_call/3</c></seealso> to handle the request.</p>
+ <seemfa marker="#Module:handle_call/3">
+ <c>Module:handle_call/3</c></seemfa> to handle the request.</p>
<p><c>ServerRef</c> can be any of the following:</p>
<list type="bulleted">
<item>The pid</item>
@@ -155,8 +157,8 @@ gen_server:abcast -----> Module:handle_cast/2
<item><c>{via,Module,ViaName}</c>, if the <c>gen_server</c> process is
registered through an alternative process registry</item>
</list>
- <p><c>Request</c> is any term that is passed as one of
- the arguments to <c>Module:handle_call/3</c>.</p>
+ <p><c>Request</c> is any term that is passed as the
+ first argument to <c>Module:handle_call/3</c>.</p>
<p><c>Timeout</c> is an integer greater than zero that
specifies how many milliseconds to wait for a reply, or
the atom <c>infinity</c> to wait indefinitely. Defaults to
@@ -190,16 +192,53 @@ gen_server:abcast -----> Module:handle_cast/2
and returns <c>ok</c> immediately, ignoring
if the destination node or <c>gen_server</c> process does not exist.
The <c>gen_server</c> process calls
- <seealso marker="#Module:handle_cast/2">
- <c>Module:handle_cast/2</c></seealso> to handle the request.</p>
+ <seemfa marker="#Module:handle_cast/2">
+ <c>Module:handle_cast/2</c></seemfa> to handle the request.</p>
<p>For a description of <c>ServerRef</c>, see
- <seealso marker="#call/2"><c>call/2,3</c></seealso>.</p>
+ <seemfa marker="#call/2"><c>call/2,3</c></seemfa>.</p>
<p><c>Request</c> is any term that is passed as one
of the arguments to <c>Module:handle_cast/2</c>.</p>
</desc>
</func>
<func>
+ <name since="OTP-23">check_response(Msg, RequestId) -> Result</name>
+ <fsummary>Check if a message is a reply from a server.</fsummary>
+ <type>
+ <v>RequestId = term()</v>
+ <v>Result = {reply, Reply} | no_reply | {error, {Reason, ServerRef}}</v>
+ <v>Msg = Reply = term()</v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = term()</v>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is used to check if a previously received
+ message, for example by <c>receive</c> or
+ <c>handle_info/2</c>, is a result of a request made with
+ <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>.
+ If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+ the result of the request is returned in <c>Reply</c>.
+ Otherwise returns <c>no_reply</c> and no cleanup is done, and
+ thus the function must be invoked repeatedly until a reply
+ is returned.
+ </p>
+ <p>
+ The return value <c>Reply</c> is defined in the return value
+ of <c>Module:handle_call/3</c>.
+ </p>
+ <p>
+ The function returns an error if the <c>gen_server</c>
+ dies before or during this request.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name since="">enter_loop(Module, Options, State)</name>
<name since="">enter_loop(Module, Options, State, ServerName)</name>
<name since="">enter_loop(Module, Options, State, Timeout)</name>
@@ -225,20 +264,20 @@ gen_server:abcast -----> Module:handle_cast/2
process receive
loop and becomes a <c>gen_server</c> process. The process
<em>must</em> have been started using one of the start functions in
- <seealso marker="proc_lib"><c>proc_lib(3)</c></seealso>. The user is
+ <seeerl marker="proc_lib"><c>proc_lib(3)</c></seeerl>. The user is
responsible for any initialization of the process, including
registering a name for it.</p>
<p>This function is useful when a more complex initialization procedure
is needed than the <c>gen_server</c> process behavior provides.</p>
<p><c>Module</c>, <c>Options</c>, and <c>ServerName</c> have
the same meanings as when calling
- <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>.
+ <seemfa marker="#start_link/3"><c>start[_link|_monitor]/3,4</c></seemfa>.
However, if <c>ServerName</c> is specified, the process must
have been registered accordingly <em>before</em> this function
is called.</p>
<p><c>State</c> and <c>Timeout</c> have the same meanings as in
the return value of
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>.
The callback module <c>Module</c> does not need to
export an <c>init/1</c> function.</p>
<p>The function fails if the calling process was not started by a
@@ -268,8 +307,8 @@ gen_server:abcast -----> Module:handle_cast/2
registered as <c>Name</c> at the specified nodes by first
sending a request to every node and then waits for
the replies. The <c>gen_server</c> process calls
- <seealso marker="#Module:handle_call/3">
- <c>Module:handle_call/3</c></seealso> to handle the request.</p>
+ <seemfa marker="#Module:handle_call/3">
+ <c>Module:handle_call/3</c></seemfa> to handle the request.</p>
<p>The function returns a tuple <c>{Replies,BadNodes}</c>, where
<c>Replies</c> is a list of <c>{Node,Reply}</c> and
<c>BadNodes</c> is a list of node that either did not exist,
@@ -280,8 +319,8 @@ gen_server:abcast -----> Module:handle_cast/2
<c>[node()|nodes()]</c>.</p>
<p><c>Name</c> is the locally registered name of each
<c>gen_server</c> process.</p>
- <p><c>Request</c> is any term that is passed as one of
- the arguments to <c>Module:handle_call/3</c>.</p>
+ <p><c>Request</c> is any term that is passed as the first
+ argument to <c>Module:handle_call/3</c>.</p>
<p><c>Timeout</c> is an integer greater than zero that
specifies how many milliseconds to wait for each reply, or
the atom <c>infinity</c> to wait indefinitely. Defaults
@@ -307,27 +346,73 @@ gen_server:abcast -----> Module:handle_cast/2
</func>
<func>
- <name since="">reply(Client, Reply) -> Result</name>
+ <name since="">reply(Client, Reply) -> ok</name>
<fsummary>Send a reply to a client.</fsummary>
<type>
<v>Client - see below</v>
<v>Reply = term()</v>
- <v>Result = term()</v>
</type>
<desc>
<p>This function can be used by a <c>gen_server</c> process to
explicitly send a reply to a client that called
- <seealso marker="#call/2"><c>call/2,3</c></seealso> or
- <seealso marker="#multi_call/2"><c>multi_call/2,3,4</c></seealso>,
+ <seemfa marker="#call/2"><c>call/2,3</c></seemfa> or
+ <seemfa marker="#multi_call/2"><c>multi_call/2,3,4</c></seemfa>,
when the reply cannot be defined in the return value of
- <seealso marker="#Module:handle_call/3">
- <c>Module:handle_call/3</c></seealso>.</p>
+ <seemfa marker="#Module:handle_call/3">
+ <c>Module:handle_call/3</c></seemfa>.</p>
<p><c>Client</c> must be the <c>From</c> argument provided to
the callback function. <c>Reply</c> is any term
given back to the client as the return value of
<c>call/2,3</c> or <c>multi_call/2,3,4</c>.</p>
- <p>The return value <c>Result</c> is not further defined, and
- is always to be ignored.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name since="OTP-23">send_request(ServerRef, Request) -> RequestId</name>
+ <fsummary>Sends a request to a generic server.</fsummary>
+ <type>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>RequestId = term()</v>
+ <v>Timeout = int()>0 | infinity</v>
+ <v>Request = term()</v>
+ </type>
+ <desc>
+ <p>
+ Sends a request to the <c>ServerRef</c> of the
+ <c>gen_server</c> process and returns a handle <c>RequestId</c>.
+ The return value <c>RequestId</c> shall later be used with
+ <seemfa marker="#wait_response/2"> <c>wait_response/2</c></seemfa> or
+ <seemfa marker="#check_response/2"> <c>check_response/2</c></seemfa>
+ to fetch the actual result of the request.
+ </p>
+ <p>
+ The call <c>gen_server:wait_response(gen_server:send_request(ServerRef,Request), Timeout)</c>
+ can be seen as equivalent to
+ <seemfa marker="#call/3"><c>gen_server:call(Server,Request,Timeout)</c></seemfa>,
+ ignoring the error handling.
+ </p>
+ <p>
+ The <c>gen_server</c> process calls
+ <seemfa marker="#Module:handle_call/3"> <c>Module:handle_call/3</c></seemfa>
+ to handle the request.
+ </p>
+ <p><c>ServerRef</c> can be any of the following:</p>
+ <list type="bulleted">
+ <item>The pid</item>
+ <item><c>Name</c>, if the <c>gen_server</c> process is locally
+ registered</item>
+ <item><c>{Name,Node}</c>, if the <c>gen_server</c> process is locally
+ registered at another node</item>
+ <item><c>{global,GlobalName}</c>, if the <c>gen_server</c> process is
+ globally registered</item>
+ <item><c>{via,Module,ViaName}</c>, if the <c>gen_server</c> process is
+ registered through an alternative process registry</item>
+ </list>
+ <p><c>Request</c> is any term that is passed as the first
+ argument to <c>Module:handle_call/3</c>.</p>
</desc>
</func>
@@ -356,7 +441,7 @@ gen_server:abcast -----> Module:handle_cast/2
<c>gen_server</c> process that is not part of a supervision tree
and thus has no supervisor.</p>
<p>For a description of arguments and return values, see
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.</p>
+ <seemfa marker="#start_link/3"><c>start_link/3,4</c></seemfa>.</p>
</desc>
</func>
@@ -387,7 +472,7 @@ gen_server:abcast -----> Module:handle_cast/2
the supervisor. For example, it ensures that
the <c>gen_server</c> process is linked to the supervisor.</p>
<p>The <c>gen_server</c> process calls
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> to
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa> to
initialize. To ensure a synchronized startup procedure,
<c>start_link/3,4</c> does not return until
<c>Module:init/1</c> has returned.</p>
@@ -399,8 +484,8 @@ gen_server:abcast -----> Module:handle_cast/2
<item>
<p>If <c>ServerName={global,GlobalName}</c>, the <c>gen_server</c>
process id registered globally as <c>GlobalName</c> using
- <seealso marker="kernel:global#register_name/2">
- <c>global:register_name/2</c></seealso> If no name is
+ <seemfa marker="kernel:global#register_name/2">
+ <c>global:register_name/2</c></seemfa> If no name is
provided, the <c>gen_server</c> process is not registered.</p>
</item>
<item>
@@ -410,14 +495,14 @@ gen_server:abcast -----> Module:handle_cast/2
<c>register_name/2</c>, <c>unregister_name/1</c>,
<c>whereis_name/1</c>, and <c>send/2</c>, which are to behave
like the corresponding functions in
- <seealso marker="kernel:global"><c>global</c></seealso>.
+ <seeerl marker="kernel:global"><c>global</c></seeerl>.
Thus, <c>{via,global,GlobalName}</c> is a valid reference.</p>
</item>
</list>
<p><c>Module</c> is the name of the callback module.</p>
<p><c>Args</c> is any term that is passed as
the argument to
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.</p>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>.</p>
<list type="bulleted">
<item>
<p>If option <c>{timeout,Time}</c> is present, the <c>gen_server</c>
@@ -429,21 +514,21 @@ gen_server:abcast -----> Module:handle_cast/2
<p>If option <c>{hibernate_after,HibernateAfterTimeout}</c> is present, the <c>gen_server</c>
process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and
if no message is received, the process goes into hibernation automatically
- (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).</p>
+ (by calling <seemfa marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seemfa>).</p>
</item>
<item>
<p>If option <c>{debug,Dbgs}</c> is present,
the corresponding <c>sys</c> function is called for each
item in <c>Dbgs</c>; see
- <seealso marker="sys"><c>sys(3)</c></seealso>.</p>
+ <seeerl marker="sys"><c>sys(3)</c></seeerl>.</p>
</item>
<item>
<p>If option <c>{spawn_opt,SOpts}</c> is present,
<c>SOpts</c> is passed as option list to
the <c>spawn_opt</c> BIF, which is used to spawn
the <c>gen_server</c> process; see
- <seealso marker="erts:erlang#spawn_opt/2">
- <c>spawn_opt/2</c></seealso>.</p>
+ <seemfa marker="erts:erlang#spawn_opt/2">
+ <c>spawn_opt/2</c></seemfa>.</p>
</item>
</list>
<note>
@@ -466,6 +551,43 @@ gen_server:abcast -----> Module:handle_cast/2
</func>
<func>
+ <name since="OTP 23.0">start_monitor(Module, Args, Options) -> Result</name>
+ <name since="OTP 23.0">start_monitor(ServerName, Module, Args, Options) -> Result</name>
+ <fsummary>Create a standalone <c>gen_server</c> process.</fsummary>
+ <type>
+ <v>ServerName = {local,Name} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName}</v>
+ <v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Module = atom()</v>
+ <v>Args = term()</v>
+ <v>Options = [Option]</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
+ <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
+ <v>&nbsp;&nbsp;SOpts = [term()]</v>
+ <v>Result = {ok,{Pid,Mon}} | ignore | {error,Error}</v>
+ <v>&nbsp;Pid = pid()</v>
+ <v>&nbsp;Error = {already_started,Pid} | term()</v>
+ </type>
+ <desc>
+ <p>Creates a standalone <c>gen_server</c> process, that is, a
+ <c>gen_server</c> process that is not part of a supervision tree
+ (and thus has no supervisor) and atomically sets up a monitor to
+ the newly created server.</p>
+ <p>For a description of arguments and return values, see
+ <seemfa marker="#start_link/3"><c>start_link/3,4</c></seemfa>.
+ Note that the return value on successful start differs from
+ <c>start_link/3,4</c>. <c>start_monitor/3,4</c> will return
+ <c>{ok,{Pid,Mon}}</c> where <c>Pid</c> is the process identifier
+ of the server, and <c>Mon</c> is a reference to the monitor
+ set up to monitor the server. If the start is not successful,
+ the caller will be blocked until the <c>DOWN</c> message has
+ been received and removed from the message queue.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="OTP 18.0">stop(ServerRef) -> ok</name>
<name since="OTP 18.0">stop(ServerRef, Reason, Timeout) -> ok</name>
<fsummary>Synchronously stop a generic server.</fsummary>
@@ -480,13 +602,13 @@ gen_server:abcast -----> Module:handle_cast/2
<desc>
<p>Orders a generic server to exit with the specified <c>Reason</c>
and waits for it to terminate. The <c>gen_server</c> process calls
- <seealso marker="#Module:terminate/2">
- <c>Module:terminate/2</c></seealso> before exiting.</p>
+ <seemfa marker="#Module:terminate/2">
+ <c>Module:terminate/2</c></seemfa> before exiting.</p>
<p>The function returns <c>ok</c> if the server terminates
with the expected reason. Any other reason than <c>normal</c>,
<c>shutdown</c>, or <c>{shutdown,Term}</c> causes an
error report to be issued using
- <seealso marker="kernel:logger"><c>logger(3)</c></seealso>.
+ <seeerl marker="kernel:logger"><c>logger(3)</c></seeerl>.
The default <c>Reason</c> is <c>normal</c>.</p>
<p><c>Timeout</c> is an integer greater than zero that
specifies how many milliseconds to wait for the server to
@@ -498,15 +620,59 @@ gen_server:abcast -----> Module:handle_cast/2
is raised.</p>
</desc>
</func>
+
+ <func>
+ <name since="OTP-23">wait_response(RequestId, Timeout) -> Result</name>
+ <fsummary>Wait for a reply from a server.</fsummary>
+ <type>
+ <v>RequestId = term()</v>
+ <v>Result = {reply, Reply} | timeout | {error, {Reason, ServerRef}}</v>
+ <v>Reply = term()</v>
+ <v>Timeout = timeout()</v>
+ <v>Reason = term()</v>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ </type>
+ <desc>
+ <p>
+ This function is used to wait for a reply of a request made with
+ <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
+ from the <c>gen_server</c> process. This function must be called
+ from the same process from which
+ <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
+ was made.
+ </p>
+ <p>
+ <c>Timeout</c> is an integer greater then or equal to zero
+ that specifies how many milliseconds to wait for an reply, or
+ the atom <c>infinity</c> to wait indefinitely.
+ If no reply is received within the specified
+ time, the function returns <c>timeout</c> and no cleanup is
+ done, and thus the function can be invoked repeatedly until a
+ reply is returned.
+ </p>
+ <p>
+ The return value <c>Reply</c> is defined in the return value
+ of <c>Module:handle_call/3</c>.
+ </p>
+ <p>
+ The function returns an error if the <c>gen_server</c>
+ dies before or during this request.
+ </p>
+ </desc>
+ </func>
</funcs>
- <section>
- <title>Callback Functions</title>
- <p>The following functions
- are to be exported from a <c>gen_server</c> callback module.</p>
- </section>
+
<funcs>
+ <fsdescription>
+ <title>Callback Functions</title>
+ <p>The following functions
+ are to be exported from a <c>gen_server</c> callback module.</p>
+ </fsdescription>
<func>
<name since="">Module:code_change(OldVsn, State, Extra) -> {ok, NewState} | {error, Reason}</name>
<fsummary>Update the internal state during upgrade/downgrade.</fsummary>
@@ -530,8 +696,8 @@ gen_server:abcast -----> Module:handle_cast/2
that is, when the instruction <c>{update,Module,Change,...}</c>,
where <c>Change={advanced,Extra}</c>, is specifed in
the <c>appup</c> file. For more information, see section
- <seealso marker="doc/design_principles:release_handling#instr">
- Release Handling Instructions</seealso> in OTP Design Principles.</p>
+ <seeguide marker="system/design_principles:release_handling#instr">
+ Release Handling Instructions</seeguide> in OTP Design Principles.</p>
<p>For an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and
for a downgrade, <c>OldVsn</c> is
<c>{down,Vsn}</c>. <c>Vsn</c> is defined by the <c>vsn</c>
@@ -570,8 +736,8 @@ gen_server:abcast -----> Module:handle_cast/2
following situations:</p>
<list type="bulleted">
<item>
- <p>One of <seealso marker="sys#get_status/1">
- <c>sys:get_status/1,2</c></seealso>
+ <p>One of <seemfa marker="sys#get_status/1">
+ <c>sys:get_status/1,2</c></seemfa>
is invoked to get the <c>gen_server</c> status. <c>Opt</c> is set
to the atom <c>normal</c>.</p>
</item>
@@ -631,8 +797,8 @@ gen_server:abcast -----> Module:handle_cast/2
</type>
<desc>
<p>Whenever a <c>gen_server</c> process receives a request sent using
- <seealso marker="#call/2"><c>call/2,3</c></seealso> or
- <seealso marker="#multi_call/2"><c>multi_call/2,3,4</c></seealso>,
+ <seemfa marker="#call/2"><c>call/2,3</c></seemfa> or
+ <seemfa marker="#multi_call/2"><c>multi_call/2,3,4</c></seemfa>,
this function is called to handle the request.</p>
<p><c>Request</c> is the <c>Request</c> argument provided
to <c>call</c> or <c>multi_call</c>.</p>
@@ -651,7 +817,7 @@ gen_server:abcast -----> Module:handle_cast/2
continues executing with the possibly updated internal state
<c>NewState</c>.</p>
<p>For a description of <c>Timeout</c> and <c>hibernate</c>, see
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.</p>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>.</p>
</item>
<item>
<p>If <c>{noreply,NewState}</c> is returned,
@@ -659,7 +825,7 @@ gen_server:abcast -----> Module:handle_cast/2
<c>{noreply,NewState,hibernate}</c>, the <c>gen_server</c>
process continues executing with <c>NewState</c>. Any reply to
<c>From</c> must be specified explicitly using
- <seealso marker="#reply/2"><c>reply/2</c></seealso>.</p>
+ <seemfa marker="#reply/2"><c>reply/2</c></seemfa>.</p>
</item>
<item>
<p>If <c>{stop,Reason,Reply,NewState}</c> is returned,
@@ -668,7 +834,7 @@ gen_server:abcast -----> Module:handle_cast/2
<item>
<p>If <c>{stop,Reason,NewState}</c> is returned, any reply
to <c>From</c> must be specified explicitly using
- <seealso marker="#reply/2"><c>reply/2</c></seealso>.
+ <seemfa marker="#reply/2"><c>reply/2</c></seemfa>.
The <c>gen_server</c> process then calls
<c>Module:terminate(Reason,NewState)</c> and terminates.</p>
</item>
@@ -693,12 +859,12 @@ gen_server:abcast -----> Module:handle_cast/2
</type>
<desc>
<p>Whenever a <c>gen_server</c> process receives a request sent using
- <seealso marker="#cast/2"><c>cast/2</c></seealso> or
- <seealso marker="#abcast/2"><c>abcast/2,3</c></seealso>,
+ <seemfa marker="#cast/2"><c>cast/2</c></seemfa> or
+ <seemfa marker="#abcast/2"><c>abcast/2,3</c></seemfa>,
this function is called to handle the request.</p>
<p>For a description of the arguments and possible return values, see
- <seealso marker="#Module:handle_call/3">
- <c>Module:handle_call/3</c></seealso>.</p>
+ <seemfa marker="#Module:handle_call/3">
+ <c>Module:handle_call/3</c></seemfa>.</p>
</desc>
</func>
@@ -732,8 +898,8 @@ gen_server:abcast -----> Module:handle_cast/2
initialization or for splitting the work in a callback in
multiple steps, updating the process state along the way.</p>
<p>For a description of the other arguments and possible return values,
- see <seealso marker="#Module:handle_call/3">
- <c>Module:handle_call/3</c></seealso>.</p>
+ see <seemfa marker="#Module:handle_call/3">
+ <c>Module:handle_call/3</c></seemfa>.</p>
</desc>
</func>
@@ -764,8 +930,8 @@ gen_server:abcast -----> Module:handle_cast/2
<p><c>Info</c> is either the atom <c>timeout</c>, if a time-out
has occurred, or the received message.</p>
<p>For a description of the other arguments and possible return values,
- see <seealso marker="#Module:handle_call/3">
- <c>Module:handle_call/3</c></seealso>.</p>
+ see <seemfa marker="#Module:handle_call/3">
+ <c>Module:handle_call/3</c></seemfa>.</p>
</desc>
</func>
@@ -782,8 +948,9 @@ gen_server:abcast -----> Module:handle_cast/2
</type>
<desc>
<p>Whenever a <c>gen_server</c> process is started using
- <seealso marker="#start/3"><c>start/3,4</c></seealso> or
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>,
+ <seemfa marker="#start/3"><c>start/3,4</c></seemfa>,
+ <seemfa marker="#start_monitor/3"><c>start_monitor/3,4</c></seemfa>,
+ or <seemfa marker="#start_link/3"><c>start_link/3,4</c></seemfa>,
this function is called by the new process to initialize.</p>
<p><c>Args</c> is the <c>Args</c> argument provided to the start
function.</p>
@@ -795,15 +962,15 @@ gen_server:abcast -----> Module:handle_cast/2
unless a request or a message is received within
<c>Timeout</c> milliseconds. A time-out is represented by
the atom <c>timeout</c>, which is to be handled by the
- <seealso marker="#Module:handle_info/2">
- <c>Module:handle_info/2</c></seealso> callback function. The atom
+ <seemfa marker="#Module:handle_info/2">
+ <c>Module:handle_info/2</c></seemfa> callback function. The atom
<c>infinity</c> can be used to wait indefinitely, this is
the default value.</p>
<p>If <c>hibernate</c> is specified instead of a time-out value,
the process goes into
hibernation when waiting for the next message to arrive (by calling
- <seealso marker="proc_lib#hibernate/3">
- <c>proc_lib:hibernate/3</c></seealso>).</p>
+ <seemfa marker="proc_lib#hibernate/3">
+ <c>proc_lib:hibernate/3</c></seemfa>).</p>
<p>If the initialization fails, the function is to return
<c>{stop,Reason}</c>, where <c>Reason</c> is any term, or
<c>ignore</c>.</p>
@@ -825,7 +992,7 @@ gen_server:abcast -----> Module:handle_cast/2
</note>
<p>This function is called by a <c>gen_server</c> process when it is
about to terminate. It is to be the opposite of
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
and do any necessary cleaning up. When it returns,
the <c>gen_server</c> process terminates with <c>Reason</c>.
The return value is ignored.</p>
@@ -860,18 +1027,18 @@ gen_server:abcast -----> Module:handle_cast/2
<c>shutdown</c>, or <c>{shutdown,Term}</c>, the <c>gen_server</c>
process is assumed to terminate because of an error and
an error report is issued using
- <seealso marker="kernel:logger"><c>logger(3)</c></seealso>.</p>
+ <seeerl marker="kernel:logger"><c>logger(3)</c></seeerl>.</p>
</desc>
</func>
</funcs>
<section>
<title>See Also</title>
- <p><seealso marker="gen_event"><c>gen_event(3)</c></seealso>,
- <seealso marker="gen_statem"><c>gen_statem(3)</c></seealso>,
- <seealso marker="proc_lib"><c>proc_lib(3)</c></seealso>,
- <seealso marker="supervisor"><c>supervisor(3)</c></seealso>,
- <seealso marker="sys"><c>sys(3)</c></seealso></p>
+ <p><seeerl marker="gen_event"><c>gen_event(3)</c></seeerl>,
+ <seeerl marker="gen_statem"><c>gen_statem(3)</c></seeerl>,
+ <seeerl marker="proc_lib"><c>proc_lib(3)</c></seeerl>,
+ <seeerl marker="supervisor"><c>supervisor(3)</c></seeerl>,
+ <seeerl marker="sys"><c>sys(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index c12ad2deef..fa3f20535d 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -34,7 +34,7 @@
<p>
<c>gen_statem</c> provides a generic state machine behaviour
that for new code replaces its predecessor
- <seealso marker="gen_fsm"><c>gen_fsm</c></seealso>
+ <seeerl marker="gen_fsm"><c>gen_fsm</c></seeerl>
since Erlang/OTP 20.0. The <c>gen_fsm</c> behaviour remains
in OTP "as is".
</p>
@@ -42,13 +42,13 @@
<p>
If you are new to <c>gen_statem</c> and want an overview
of concepts and operation the section
- <seealso marker="doc/design_principles:statem">
+ <seeguide marker="system/design_principles:statem">
<c>gen_statem</c>&nbsp;Behaviour
- </seealso>
+ </seeguide>
located in the User's Guide
- <seealso marker="doc/design_principles:users_guide">
+ <seeguide marker="system/design_principles:index">
OTP Design Principles
- </seealso>
+ </seeguide>
is recommended to read before this reference manual,
possibly after the Description section you are reading here.
</p>
@@ -59,13 +59,13 @@
However, the generated descriptions also reflect the type hierarchy,
which sometimes makes it hard to get a good overview.
If so, see the section
- <seealso marker="doc/design_principles:statem">
+ <seeguide marker="system/design_principles:statem">
<c>gen_statem</c>&nbsp;Behaviour
- </seealso>
+ </seeguide>
in the
- <seealso marker="doc/design_principles:users_guide">
+ <seeguide marker="system/design_principles:index">
OTP Design Principles
- </seealso>
+ </seeguide>
User's Guide.
</p>
<note>
@@ -74,44 +74,44 @@
<item>
In OTP 19.1 a backwards incompatible change of
the return tuple from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
was made and the mandatory callback function
- <seealso marker="#Module:callback_mode/0">
+ <seemfa marker="#Module:callback_mode/0">
<c>Module:callback_mode/0</c>
- </seealso>
+ </seemfa>
was introduced.
</item>
<item>
In OTP 20.0
- <seealso marker="#type-generic_timeout">
+ <seetype marker="#generic_timeout">
generic time-outs
- </seealso>
+ </seetype>
were added.
</item>
<item>
In OTP 22.1 time-out content
- <seealso marker="#type-timeout_update_action">
+ <seetype marker="#timeout_update_action">
<c>update</c>
- </seealso>
+ </seetype>
and explicit time-out
- <seealso marker="#type-timeout_cancel_action">
+ <seetype marker="#timeout_cancel_action">
<c>cancel</c>
- </seealso>
+ </seetype>
were added.
</item>
<item>
In OTP 22.3 the possibility to change the callback module
with actions
- <seealso marker="#type-action"><c>change_callback_module</c></seealso>,
- <seealso marker="#type-action"><c>push_callback_module</c></seealso> and
- <seealso marker="#type-action"><c>pop_callback_module</c></seealso>,
+ <seetype marker="#action"><c>change_callback_module</c></seetype>,
+ <seetype marker="#action"><c>push_callback_module</c></seetype> and
+ <seetype marker="#action"><c>pop_callback_module</c></seetype>,
was added.
</item>
</list>
</note>
<p>
<c>gen_statem</c> has got the same features that
- <seealso marker="gen_fsm"><c>gen_fsm</c></seealso>
+ <seeerl marker="gen_fsm"><c>gen_fsm</c></seeerl>
had and adds some really useful:
</p>
<list type="bulleted">
@@ -133,14 +133,14 @@
<p>
Two
- <seealso marker="#type-callback_mode"><em>callback modes</em></seealso>
+ <seetype marker="#callback_mode"><em>callback modes</em></seetype>
are supported:
</p>
<list type="bulleted">
<item>
<p>
One for finite-state machines
- (<seealso marker="gen_fsm"><c>gen_fsm</c></seealso> like),
+ (<seeerl marker="gen_fsm"><c>gen_fsm</c></seeerl> like),
which requires the state to be an atom and uses that state as
the name of the current callback function.
</p>
@@ -154,18 +154,18 @@
</list>
<p>
The callback model(s) for <c>gen_statem</c> differs from
- the one for <seealso marker="gen_fsm"><c>gen_fsm</c></seealso>,
+ the one for <seeerl marker="gen_fsm"><c>gen_fsm</c></seeerl>,
but it is still fairly easy to
- <seealso marker="gen_fsm#Migration to gen_statem">
+ <seeerl marker="gen_fsm#Migration to gen_statem">
rewrite from
- </seealso> <c>gen_fsm</c> to <c>gen_statem</c>.
+ </seeerl> <c>gen_fsm</c> to <c>gen_statem</c>.
</p>
<p>
A generic state machine server process (<c>gen_statem</c>) implemented
using this module has a standard set of interface functions
and includes functionality for tracing and error reporting.
It also fits into an OTP supervision tree. For more information, see
- <seealso marker="doc/design_principles:statem">OTP Design Principles</seealso>.
+ <seeguide marker="system/design_principles:statem">OTP Design Principles</seeguide>.
</p>
<p>
A <c>gen_statem</c> assumes all specific parts to be located in a
@@ -176,6 +176,7 @@
gen_statem module Callback module
----------------- ---------------
gen_statem:start
+gen_statem:start_monitor
gen_statem:start_link -----> Module:init/1
Server start or code change
@@ -185,6 +186,7 @@ gen_statem:stop -----> Module:terminate/3
gen_statem:call
gen_statem:cast
+gen_statem:send_request
erlang:send
erlang:'!' -----> Module:StateName/3
Module:handle_event/4
@@ -194,7 +196,7 @@ erlang:'!' -----> Module:StateName/3
- -----> Module:code_change/4</pre>
<p>
Events are of different
- <seealso marker="#type-event_type">types</seealso>,
+ <seetype marker="#event_type">types</seetype>,
so the callback functions can know the origin of an event
and how to respond.
</p>
@@ -202,39 +204,39 @@ erlang:'!' -----> Module:StateName/3
If a callback function fails or returns a bad value,
the <c>gen_statem</c> terminates, unless otherwise stated.
However, an exception of class
- <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>
+ <seemfa marker="erts:erlang#throw/1"><c>throw</c></seemfa>
is not regarded as an error but as a valid return
from all callback functions.
</p>
<marker id="state callback"/>
<p>
The <em>state callback</em> for a specific
- <seealso marker="#type-state">state</seealso>
+ <seetype marker="#state">state</seetype>
in a <c>gen_statem</c> is the callback function that is called
for all events in this state. It is selected depending on which
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
that the callback module defines with the callback function
- <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>.
+ <seemfa marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seemfa>.
</p>
<p>
When the
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
is <c>state_functions</c>, the state must be an atom and
is used as the <em>state callback</em> name; see
- <seealso marker="#Module:StateName/3"><c>Module:StateName/3</c></seealso>.
+ <seemfa marker="#Module:StateName/3"><c>Module:StateName/3</c></seemfa>.
This co-locates all code for a specific state
in one function as the <c>gen_statem</c> engine
branches depending on state name.
Note the fact that the callback function
- <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ <seemfa marker="#Module:terminate/3"><c>Module:terminate/3</c></seemfa>
makes the state name <c>terminate</c> unusable in this mode.
</p>
<p>
When the
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
is <c>handle_event_function</c>, the state can be any term
and the <em>state callback</em> name is
- <seealso marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seealso>.
+ <seemfa marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seemfa>.
This makes it easy to branch depending on state or event as you desire.
Be careful about which events you handle in which
states so that you do not accidentally postpone an event
@@ -243,10 +245,10 @@ erlang:'!' -----> Module:StateName/3
<p>
When <c>gen_statem</c> receives a process message it is
converted into an event and the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
is called with the event as two arguments: type and content.
When the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
has processed the event it returns to <c>gen_statem</c>
which does a <em>state transition</em>.
If this <em>state transition</em> is to a different state,
@@ -254,13 +256,13 @@ erlang:'!' -----> Module:StateName/3
</p>
<p>
The
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
may return
- <seealso marker="#type-action"><em>transition actions</em></seealso>
+ <seetype marker="#action"><em>transition actions</em></seetype>
for <c>gen_statem</c>
to execute during the <em>state transition</em>,
for example to reply to a
- <seealso marker="#call/2"><c>gen_statem:call/2,3</c></seealso>.
+ <seemfa marker="#call/2"><c>gen_statem:call/2,3</c></seemfa>.
</p>
<p>
One of the possible <em>transition actions</em>
@@ -281,17 +283,17 @@ erlang:'!' -----> Module:StateName/3
</p>
<p>
The
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
can insert events using the
- <seealso marker="#type-action"><em>transition actions</em></seealso>
+ <seetype marker="#action"><em>transition actions</em></seetype>
<c>next_event</c>
and such an event is inserted in the event queue
as the next to call the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
with.
That is, as if it is the oldest incoming event.
A dedicated
- <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <seetype marker="#event_type"><c>event_type()</c></seetype>
<c>internal</c> can be used for such events making them impossible
to mistake for external events.
</p>
@@ -299,15 +301,15 @@ erlang:'!' -----> Module:StateName/3
Inserting an event replaces the trick of calling your own
state handling functions that you often would have to
resort to in, for example,
- <seealso marker="gen_fsm"><c>gen_fsm</c></seealso>
+ <seeerl marker="gen_fsm"><c>gen_fsm</c></seeerl>
to force processing an inserted event before others.
</p>
<p>
The <c>gen_statem</c> engine can automatically
make a specialized call to the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
whenever a new state is entered; see
- <seealso marker="#type-state_enter"><c>state_enter()</c></seealso>.
+ <seetype marker="#state_enter"><c>state_enter()</c></seetype>.
This is for writing code common to all state entries.
Another way to do it is to explicitly insert an event
at the <em>state transition</em>,
@@ -325,18 +327,18 @@ erlang:'!' -----> Module:StateName/3
</note>
<p>
For the details of a <em>state transition</em>, see type
- <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>.
+ <seetype marker="#transition_option"><c>transition_option()</c></seetype>.
</p>
<p>
A <c>gen_statem</c> handles system messages as described in
- <seealso marker="sys"><c>sys</c></seealso>.
+ <seeerl marker="sys"><c>sys</c></seeerl>.
The <c>sys</c> module can be used for debugging a <c>gen_statem</c>.
</p>
<p>
Notice that a <c>gen_statem</c> does not trap exit signals
automatically, this must be explicitly initiated in
the callback module (by calling
- <seealso marker="erts:erlang#process_flag/2"><c>process_flag(trap_exit, true)</c></seealso>.
+ <seemfa marker="erts:erlang#process_flag/2"><c>process_flag(trap_exit, true)</c></seemfa>.
</p>
<p>
Unless otherwise stated, all functions in this module fail if
@@ -345,29 +347,30 @@ erlang:'!' -----> Module:StateName/3
</p>
<p>
The <c>gen_statem</c> process can go into hibernation; see
- <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>.
+ <seemfa marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seemfa>.
It is done when a
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
or
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
specifies <c>hibernate</c> in the returned
- <seealso marker="#type-action"><c>Actions</c></seealso>
+ <seetype marker="#action"><c>Actions</c></seetype>
list. This feature can be useful to reclaim process heap memory
while the server is expected to be idle for a long time.
However, use this feature with care,
as hibernation can be too costly
to use after every event; see
- <seealso marker="erts:erlang#hibernate/3"><c>erlang:hibernate/3</c></seealso>.
+ <seemfa marker="erts:erlang#hibernate/3"><c>erlang:hibernate/3</c></seemfa>.
</p>
<p>
There is also a server start option
- <seealso marker="#type-enter_loop_opt">
+ <seetype marker="#enter_loop_opt">
<c>{hibernate_after, Timeout}</c>
- </seealso>
+ </seetype>
for
- <seealso marker="#start/3"><c>start/3,4</c></seealso>,
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso> or
- <seealso marker="#enter_loop/4"><c>enter_loop/4,5,6</c></seealso>,
+ <seemfa marker="#start/3"><c>start/3,4</c></seemfa>,
+ <seemfa marker="#start_monitor/3"><c>start_monitor/3,4</c></seemfa>,
+ <seemfa marker="#start_link/3"><c>start_link/3,4</c></seemfa> or
+ <seemfa marker="#enter_loop/4"><c>enter_loop/4,5,6</c></seemfa>,
that may be used to automatically hibernate the server.
</p>
</description>
@@ -377,7 +380,7 @@ erlang:'!' -----> Module:StateName/3
<p>
The following example shows a simple pushbutton model
for a toggling pushbutton implemented with
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
<c>state_functions</c>.
You can push the button and it replies if it went on or off,
and you can ask for a count of how many times it has been
@@ -463,7 +466,7 @@ ok
</pre>
<p>
To compare styles, here follows the same example using
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
<c>handle_event_function</c>, or rather the code to replace
after function <c>init/1</c> of the <c>pushbutton.erl</c>
example file above:
@@ -498,9 +501,9 @@ handle_event(_, _, State, Data) ->
<p>
Name specification to use when starting
a <c>gen_statem</c> server. See
- <seealso marker="#start_link/3"><c>start_link/3</c></seealso>
+ <seemfa marker="#start_link/3"><c>start_link/3</c></seemfa>
and
- <seealso marker="#type-server_ref"><c>server_ref()</c></seealso>
+ <seetype marker="#server_ref"><c>server_ref()</c></seetype>
below.
</p>
</desc>
@@ -511,8 +514,8 @@ handle_event(_, _, State, Data) ->
<p>
Server specification to use when addressing
a <c>gen_statem</c> server.
- See <seealso marker="#call/2"><c>call/2</c></seealso> and
- <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ See <seemfa marker="#call/2"><c>call/2</c></seemfa> and
+ <seetype marker="#server_name"><c>server_name()</c></seetype>
above.
</p>
<p>It can be:</p>
@@ -534,7 +537,7 @@ handle_event(_, _, State, Data) ->
<item>
<p>
The <c>gen_statem</c> is globally registered in
- <seealso marker="kernel:global"><c>global</c></seealso>.
+ <seeerl marker="kernel:global"><c>global</c></seeerl>.
</p>
</item>
<tag><c>{via,RegMod,ViaName}</c></tag>
@@ -547,7 +550,7 @@ handle_event(_, _, State, Data) ->
<c>register_name/2</c>, <c>unregister_name/1</c>,
<c>whereis_name/1</c>, and <c>send/2</c>,
which are to behave like the corresponding functions in
- <seealso marker="kernel:global"><c>global</c></seealso>.
+ <seeerl marker="kernel:global"><c>global</c></seeerl>.
Thus, <c>{via,global,GlobalName}</c> is the same as
<c>{global,GlobalName}</c>.
</p>
@@ -561,7 +564,7 @@ handle_event(_, _, State, Data) ->
<p>
Options that can be used when starting
a <c>gen_statem</c> server through, for example,
- <seealso marker="#start_link/3"><c>start_link/3</c></seealso>.
+ <seemfa marker="#start_link/3"><c>start_link/3</c></seemfa>.
</p>
</desc>
</datatype>
@@ -569,8 +572,18 @@ handle_event(_, _, State, Data) ->
<name name="start_ret"/>
<desc>
<p>
- Return value from the start functions, for example,
- <seealso marker="#start_link/3"><c>start_link/3</c></seealso>.
+ Return value from the <c>start()</c> and <c>start_link()</c> functions,
+ for example, <seemfa marker="#start_link/3"><c>start_link/3</c></seemfa>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="start_mon_ret"/>
+ <desc>
+ <p>
+ Return value from the
+ <seemfa marker="#start_monitor/3"><c>start_monitor()</c></seemfa>
+ functions.
</p>
</desc>
</datatype>
@@ -580,7 +593,7 @@ handle_event(_, _, State, Data) ->
<p>
Options that can be used when starting
a <c>gen_statem</c> server through,
- <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>.
+ <seemfa marker="#enter_loop/4"><c>enter_loop/4-6</c></seemfa>.
</p>
<taglist>
<tag><c>hibernate_after</c></tag>
@@ -591,7 +604,7 @@ handle_event(_, _, State, Data) ->
any message for <c>HibernateAfterTimeout</c> milliseconds and
if no message is received, the process goes into hibernation
automatically (by calling
- <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).
+ <seemfa marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seemfa>).
</p>
</item>
<tag><c>debug</c></tag>
@@ -599,7 +612,7 @@ handle_event(_, _, State, Data) ->
<p>
For every entry in <c><anno>Dbgs</anno></c>,
the corresponding function in
- <seealso marker="sys"><c>sys</c></seealso> is called.
+ <seeerl marker="sys"><c>sys</c></seeerl> is called.
</p>
</item>
</taglist>
@@ -610,10 +623,10 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Destination to use when replying through, for example, the
- <seealso marker="#type-action"><c>action()</c></seealso>
+ <seetype marker="#action"><c>action()</c></seetype>
<c>{reply,From,Reply}</c>
to a process that has called the <c>gen_statem</c> server using
- <seealso marker="#call/2"><c>call/2</c></seealso>.
+ <seemfa marker="#call/2"><c>call/2</c></seemfa>.
</p>
</desc>
</datatype>
@@ -622,7 +635,7 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
If the
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
is <c>handle_event_function</c>,
the state can be any term.
After a <em>state change</em> (<c>NextState =/= State</c>),
@@ -635,14 +648,14 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
If the
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
is <c>state_functions</c>,
the state must be an atom.
After a <em>state change</em> (<c>NextState =/= State</c>),
all postponed events are retried.
Note that the state <c>terminate</c> is not possible
to use since it would collide with the optional callback function
- <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>.
+ <seemfa marker="#Module:terminate/3"><c>Module:terminate/3</c></seemfa>.
</p>
</desc>
</datatype>
@@ -652,7 +665,7 @@ handle_event(_, _, State, Data) ->
<p>
A term in which the state machine implementation
is to store any server data it needs. The difference between
- this and the <seealso marker="#type-state"><c>state()</c></seealso>
+ this and the <seetype marker="#state"><c>state()</c></seetype>
itself is that a change in this data does not cause
postponed events to be retried. Hence, if a change
in this data would change the set of events that
@@ -666,14 +679,14 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
There are 3 categories of events:
- <seealso marker="#type-external_event_type">external</seealso>,
- <seealso marker="#type-timeout_event_type">timeout</seealso>,
+ <seetype marker="#external_event_type">external</seetype>,
+ <seetype marker="#timeout_event_type">timeout</seetype>,
and <c>internal</c>.
</p>
<p>
<c>internal</c> events can only be generated by the
state machine itself through the <em>transition action</em>
- <seealso marker="#type-action"><c>next_event</c></seealso>.
+ <seetype marker="#action"><c>next_event</c></seetype>.
</p>
</desc>
</datatype>
@@ -683,11 +696,12 @@ handle_event(_, _, State, Data) ->
<p>
External events are of 3 types:
<c>{call,<anno>From</anno>}</c>, <c>cast</c>, or <c>info</c>.
- <seealso marker="#call/2">Calls</seealso>
- (synchronous) and
- <seealso marker="#cast/2">casts</seealso>
- originate from the corresponding API functions.
+ Type <c>call</c> originates from the API functions
+ <seemfa marker="#call/2"><c>call/2</c></seemfa>
+ and <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>.
For calls, the event contains whom to reply to.
+ Type <c>cast</c> originates from the API function
+ <seemfa marker="#cast/2"><c>cast/2</c></seemfa>.
Type <c>info</c> originates from regular process messages sent
to the <c>gen_statem</c>.
</p>
@@ -699,7 +713,7 @@ handle_event(_, _, State, Data) ->
<p>
There are 3 types of time-out events that the state machine
can generate for itself with the corresponding
- <seealso marker="#type-timeout_action">timeout_action()</seealso>s.
+ <seetype marker="#timeout_action">timeout_action()</seetype>s.
</p>
</desc>
</datatype>
@@ -708,11 +722,11 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
This is the return type from
- <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>
+ <seemfa marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seemfa>
and selects
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
and whether to do
- <seealso marker="#type-state_enter"><em>state enter calls</em></seealso>,
+ <seetype marker="#state_enter"><em>state enter calls</em></seetype>,
or not.
</p>
</desc>
@@ -723,16 +737,16 @@ handle_event(_, _, State, Data) ->
<p>
The <em>callback mode</em> is selected
with the return value from
- <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>:
+ <seemfa marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seemfa>:
</p>
<taglist>
<tag><c>state_functions</c></tag>
<item>
<p>
The state must be of type
- <seealso marker="#type-state_name"><c>state_name()</c></seealso>
+ <seetype marker="#state_name"><c>state_name()</c></seetype>
and one callback function per state, that is,
- <seealso marker="#Module:StateName/3"><c>Module:StateName/3</c></seealso>,
+ <seemfa marker="#Module:StateName/3"><c>Module:StateName/3</c></seemfa>,
is used.
</p>
</item>
@@ -740,22 +754,22 @@ handle_event(_, _, State, Data) ->
<item>
<p>
The state can be any term and the callback function
- <seealso marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seealso>
+ <seemfa marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seemfa>
is used for all states.
</p>
</item>
</taglist>
<p>
The function
- <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>
+ <seemfa marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seemfa>
is called when starting the <c>gen_statem</c>,
after code change
and after changing the callback module with any of the actions
- <seealso marker="#type-action"><c>change_callback_module</c></seealso>,
- <seealso marker="#type-action"><c>push_callback_module</c></seealso> or
- <seealso marker="#type-action"><c>pop_callback_module</c></seealso>.
+ <seetype marker="#action"><c>change_callback_module</c></seetype>,
+ <seetype marker="#action"><c>push_callback_module</c></seetype> or
+ <seetype marker="#action"><c>pop_callback_module</c></seetype>.
The result is cached for subsequent calls to
- <seealso marker="#state callback">state callbacks</seealso>.
+ <seeerl marker="#state callback">state callbacks</seeerl>.
</p>
</desc>
</datatype>
@@ -766,45 +780,45 @@ handle_event(_, _, State, Data) ->
Whether the state machine should use <em>state enter calls</em>
or not is selected when starting the <c>gen_statem</c>
and after code change using the return value from
- <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>.
+ <seemfa marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seemfa>.
</p>
<p>
If
- <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>
+ <seemfa marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seemfa>
returns a list containing <c>state_enter</c>,
the <c>gen_statem</c> engine will, at every <em>state change</em>,
call the
- <seealso marker="#state callback">state callback</seealso>
+ <seeerl marker="#state callback">state callback</seeerl>
with arguments <c>(enter, OldState, Data)</c> or
<c>(enter, OldState, State, Data)</c>, depending on the
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>.
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>.
This may look like an event but is really a call
performed after the previous
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
returned and before any event is delivered to the new
- <seealso marker="#state callback"><em>state callback</em></seealso>.
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>.
See
- <seealso marker="#Module:StateName/3"><c>Module:StateName/3</c></seealso>
+ <seemfa marker="#Module:StateName/3"><c>Module:StateName/3</c></seemfa>
and
- <seealso marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seealso>.
+ <seemfa marker="#Module:handle_event/4"><c>Module:handle_event/4</c></seemfa>.
Such a call can be repeated by returning a
- <seealso marker="#type-state_callback_result">
+ <seetype marker="#state_callback_result">
<c>repeat_state</c>
- </seealso>
+ </seetype>
or
- <seealso marker="#type-state_callback_result">
+ <seetype marker="#state_callback_result">
<c>repeat_state_and_data</c>
- </seealso>
+ </seetype>
tuple from the <em>state callback</em>.
</p>
<p>
If
- <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>
+ <seemfa marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seemfa>
does not return such a list, no <em>state enter calls</em> are done.
</p>
<p>
If
- <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>
+ <seemfa marker="#Module:code_change/4"><c>Module:code_change/4</c></seemfa>
should transform the state,
it is regarded as a state rename and not a <em>state change</em>,
which will not cause a <em>state enter call</em>.
@@ -824,10 +838,10 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Transition options can be set by
- <seealso marker="#type-action">actions</seealso>
+ <seetype marker="#action">actions</seetype>
and modify the <em>state transition</em>.
The <em>state transition</em> takes place when the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
has processed an event and returns.
Here are the sequence of steps for a <em>state transition</em>:
</p>
@@ -835,10 +849,10 @@ handle_event(_, _, State, Data) ->
<item>
<p>
All returned
- <seealso marker="#type-action">actions</seealso>
+ <seetype marker="#action">actions</seetype>
are processed in order of appearance.
In this step all replies generated by any
- <seealso marker="#type-reply_action"><c>reply_action()</c></seealso>
+ <seetype marker="#reply_action"><c>reply_action()</c></seetype>
are sent. Other actions set <c>transition_option()</c>s
that come into play in subsequent steps.
</p>
@@ -846,45 +860,45 @@ handle_event(_, _, State, Data) ->
<item>
<p>
If
- <seealso marker="#type-state_enter">
+ <seetype marker="#state_enter">
<em>state enter calls</em>
- </seealso>
+ </seetype>
are used, and either
it is the initial state or one of the callback results
- <seealso marker="#type-state_callback_result">
+ <seetype marker="#state_callback_result">
<c>repeat_state_and_data</c>
- </seealso>
+ </seetype>
or
- <seealso marker="#type-state_callback_result">
+ <seetype marker="#state_callback_result">
<c>repeat_state_and_data</c>
- </seealso>
+ </seetype>
is used the <c>gen_statem</c> engine calls
the current state callback with arguments
- <seealso marker="#type-state_enter"><c>(enter, State, Data)</c></seealso>
+ <seetype marker="#state_enter"><c>(enter, State, Data)</c></seetype>
or
- <seealso marker="#type-state_enter"><c>(enter, State, State, Data)</c></seealso>
- (depending on <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>)
+ <seetype marker="#state_enter"><c>(enter, State, State, Data)</c></seetype>
+ (depending on <seetype marker="#callback_mode"><em>callback mode</em></seetype>)
and when it returns starts again from the top of this sequence.
</p>
<p>
If
- <seealso marker="#type-state_enter">
+ <seetype marker="#state_enter">
<em>state enter calls</em>
- </seealso>
+ </seetype>
are used, and the state changes
the <c>gen_statem</c> engine calls
the new state callback with arguments
- <seealso marker="#type-state_enter"><c>(enter, OldState, Data)</c></seealso>
+ <seetype marker="#state_enter"><c>(enter, OldState, Data)</c></seetype>
or
- <seealso marker="#type-state_enter"><c>(enter, OldState, State, Data)</c></seealso>
- (depending on <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>)
+ <seetype marker="#state_enter"><c>(enter, OldState, State, Data)</c></seetype>
+ (depending on <seetype marker="#callback_mode"><em>callback mode</em></seetype>)
and when it returns starts again from the top of this sequence.
</p>
</item>
<item>
<p>
If
- <seealso marker="#type-postpone"><c>postpone()</c></seealso>
+ <seetype marker="#postpone"><c>postpone()</c></seetype>
is <c>true</c>,
the current event is postponed.
</p>
@@ -899,7 +913,7 @@ handle_event(_, _, State, Data) ->
<item>
<p>
All events stored with
- <seealso marker="#type-action"><c>action()</c></seealso>
+ <seetype marker="#action"><c>action()</c></seetype>
<c>next_event</c>
are inserted to be processed before previously queued events.
</p>
@@ -907,10 +921,10 @@ handle_event(_, _, State, Data) ->
<item>
<p>
Time-out timers
- <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>,
- <seealso marker="#type-generic_timeout"><c>generic_timeout()</c></seealso>
+ <seetype marker="#event_timeout"><c>event_timeout()</c></seetype>,
+ <seetype marker="#generic_timeout"><c>generic_timeout()</c></seetype>
and
- <seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
+ <seetype marker="#state_timeout"><c>state_timeout()</c></seetype>
are handled. Time-outs with zero time are guaranteed to be
delivered to the state machine before any external
not yet received event so if there is such a time-out requested,
@@ -921,23 +935,23 @@ handle_event(_, _, State, Data) ->
</p>
<p>
Any event cancels an
- <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
+ <seetype marker="#event_timeout"><c>event_timeout()</c></seetype>
so a zero time event time-out is only generated
if the event queue is empty.
</p>
<p>
A <em>state change</em> cancels a
- <seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
+ <seetype marker="#state_timeout"><c>state_timeout()</c></seetype>
and any new transition option of this type
belongs to the new state, that is; a
- <seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
+ <seetype marker="#state_timeout"><c>state_timeout()</c></seetype>
applies to the state the state machine enters.
</p>
</item>
<item>
<p>
If there are enqueued events the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
for the possibly new state
is called with the oldest enqueued event,
and we start again from the top of this sequence.
@@ -948,14 +962,14 @@ handle_event(_, _, State, Data) ->
Otherwise the <c>gen_statem</c> goes into <c>receive</c>
or hibernation
(if
- <seealso marker="#type-hibernate"><c>hibernate()</c></seealso>
+ <seetype marker="#hibernate"><c>hibernate()</c></seetype>
is <c>true</c>)
to wait for the next message. In hibernation the next
non-system event awakens the <c>gen_statem</c>, or rather
the next incoming message awakens the <c>gen_statem</c>,
but if it is a system event it goes right back into hibernation.
When a new message arrives the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
is called with the corresponding event,
and we start again from the top of this sequence.
</p>
@@ -979,7 +993,7 @@ handle_event(_, _, State, Data) ->
<p>
If <c>true</c>, hibernates the <c>gen_statem</c>
by calling
- <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>
+ <seemfa marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seemfa>
before going into <c>receive</c>
to wait for a new external event.
</p>
@@ -988,9 +1002,9 @@ handle_event(_, _, State, Data) ->
If there are enqueued events to process
when hibrnation is requested,
this is optimized by not hibernating but instead calling
- <seealso marker="erts:erlang#garbage_collect/0">
+ <seemfa marker="erts:erlang#garbage_collect/0">
<c>erlang:garbage_collect/0</c>
- </seealso>
+ </seemfa>
to simulate that the <c>gen_statem</c> entered hibernation
and immediately got awakened by an enqueued event.
</p>
@@ -1002,15 +1016,15 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Starts a timer set by
- <seealso marker="#type-enter_action"><c>enter_action()</c></seealso>
+ <seetype marker="#enter_action"><c>enter_action()</c></seetype>
<c>timeout</c>.
When the timer expires an event of
- <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <seetype marker="#event_type"><c>event_type()</c></seetype>
<c>timeout</c> will be generated.
See
- <seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer/4</c></seealso>
+ <seemfa marker="erts:erlang#start_timer/4"><c>erlang:start_timer/4</c></seemfa>
for how <c>Time</c> and
- <seealso marker="#type-timeout_option"><c>Options</c></seealso>
+ <seetype marker="#timeout_option"><c>Options</c></seetype>
are interpreted. Future <c>erlang:start_timer/4</c> <c>Options</c>
will not necessarily be supported.
</p>
@@ -1042,15 +1056,15 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Starts a timer set by
- <seealso marker="#type-enter_action"><c>enter_action()</c></seealso>
+ <seetype marker="#enter_action"><c>enter_action()</c></seetype>
<c>{timeout,Name}</c>.
When the timer expires an event of
- <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <seetype marker="#event_type"><c>event_type()</c></seetype>
<c>{timeout,Name}</c> will be generated.
See
- <seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer/4</c></seealso>
+ <seemfa marker="erts:erlang#start_timer/4"><c>erlang:start_timer/4</c></seemfa>
for how <c>Time</c> and
- <seealso marker="#type-timeout_option"><c>Options</c></seealso>
+ <seetype marker="#timeout_option"><c>Options</c></seetype>
are interpreted. Future <c>erlang:start_timer/4</c> <c>Options</c>
will not necessarily be supported.
</p>
@@ -1078,15 +1092,15 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Starts a timer set by
- <seealso marker="#type-enter_action"><c>enter_action()</c></seealso>
+ <seetype marker="#enter_action"><c>enter_action()</c></seetype>
<c>state_timeout</c>.
When the timer expires an event of
- <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <seetype marker="#event_type"><c>event_type()</c></seetype>
<c>state_timeout</c> will be generated.
See
- <seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer/4</c></seealso>
+ <seemfa marker="erts:erlang#start_timer/4"><c>erlang:start_timer/4</c></seemfa>
for how <c>Time</c> and
- <seealso marker="#type-timeout_option"><c>Options</c></seealso>
+ <seetype marker="#timeout_option"><c>Options</c></seetype>
are interpreted. Future <c>erlang:start_timer/4</c> <c>Options</c>
will not necessarily be supported.
</p>
@@ -1115,9 +1129,9 @@ handle_event(_, _, State, Data) ->
If <c>Abs</c> is <c>true</c> an absolute timer is started,
and if it is <c>false</c> a relative, which is the default.
See
- <seealso marker="erts:erlang#start_timer/4">
+ <seemfa marker="erts:erlang#start_timer/4">
<c>erlang:start_timer/4</c>
- </seealso>
+ </seemfa>
for details.
</p>
<p>
@@ -1130,26 +1144,26 @@ handle_event(_, _, State, Data) ->
<p>
These <em>transition actions</em> can be invoked by
returning them from the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
when it is called with an
- <seealso marker="#type-event_type">event</seealso>,
+ <seetype marker="#event_type">event</seetype>,
from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
or by giving them to
- <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
+ <seemfa marker="#enter_loop/5"><c>enter_loop/5,6</c></seemfa>.
</p>
<p>
Actions are executed in the containing list order.
</p>
<p>
Actions that set
- <seealso marker="#type-transition_option">
+ <seetype marker="#transition_option">
transition options
- </seealso>
+ </seetype>
override any previous of the same type,
so the last in the containing list wins.
For example, the last
- <seealso marker="#type-postpone"><c>postpone()</c></seealso>
+ <seetype marker="#postpone"><c>postpone()</c></seetype>
overrides any previous <c>postpone()</c> in the list.
</p>
<taglist>
@@ -1157,15 +1171,15 @@ handle_event(_, _, State, Data) ->
<item>
<p>
Sets the
- <seealso marker="#type-transition_option">
+ <seetype marker="#transition_option">
<c>transition_option()</c>
- </seealso>
- <seealso marker="#type-postpone"><c>postpone()</c></seealso>
+ </seetype>
+ <seetype marker="#postpone"><c>postpone()</c></seetype>
for this <em>state transition</em>.
This action is ignored when returned from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
or given to
- <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>,
+ <seemfa marker="#enter_loop/5"><c>enter_loop/5,6</c></seemfa>,
as there is no event to postpone in those cases.
</p>
</item>
@@ -1173,9 +1187,9 @@ handle_event(_, _, State, Data) ->
<item>
<p>
This action does not set any
- <seealso marker="#type-transition_option">
+ <seetype marker="#transition_option">
<c>transition_option()</c>
- </seealso>
+ </seetype>
but instead stores the specified <c><anno>EventType</anno></c>
and <c><anno>EventContent</anno></c> for insertion after all
actions have been executed.
@@ -1188,7 +1202,7 @@ handle_event(_, _, State, Data) ->
</p>
<p>
An event of type
- <seealso marker="#type-event_type"><c>internal</c></seealso>
+ <seetype marker="#event_type"><c>internal</c></seetype>
is to be used when you want to reliably distinguish
an event inserted this way from any external event.
</p>
@@ -1201,19 +1215,19 @@ handle_event(_, _, State, Data) ->
Changes the callback module to
<c><anno>NewModule</anno></c>
which will be used when calling all subsequent
- <seealso marker="#state callback">state callbacks</seealso>.
+ <seeerl marker="#state callback">state callbacks</seeerl>.
</p>
<p>
The <c>gen_statem</c> engine will find out the
- <seealso marker="#type-callback_mode">
+ <seetype marker="#callback_mode">
<em>callback mode</em>
- </seealso>
+ </seetype>
of <c><anno>NewModule</anno></c> by calling
- <seealso marker="#Module:callback_mode/0">
+ <seemfa marker="#Module:callback_mode/0">
<c>NewModule:callback_mode/0</c>
- </seealso>
+ </seemfa>
before the next
- <seealso marker="#state callback">state callback</seealso>.
+ <seeerl marker="#state callback">state callback</seeerl>.
</p>
<p>
Changing the callback module does not affect the
@@ -1222,21 +1236,21 @@ handle_event(_, _, State, Data) ->
Be aware that all relevant callback functions in
<c><anno>NewModule</anno></c>
such as the
- <seealso marker="#state callback">state callback</seealso>,
- <seealso marker="#Module:code_change/4"><c><anno>NewModule</anno>:code_change/4</c></seealso>,
- <seealso marker="#Module:format_status/2">
+ <seeerl marker="#state callback">state callback</seeerl>,
+ <seemfa marker="#Module:code_change/4"><c><anno>NewModule</anno>:code_change/4</c></seemfa>,
+ <seemfa marker="#Module:format_status/2">
<c><anno>NewModule</anno>:format_status/2</c>
- </seealso>
+ </seemfa>
and
- <seealso marker="#Module:terminate/3">
+ <seemfa marker="#Module:terminate/3">
<c><anno>NewModule</anno>:terminate/3</c>
- </seealso>
+ </seemfa>
must be able to handle the state and data
from the old module.
</p>
</item>
<tag>
- <c>push_callback_module</c><br />
+ <c>push_callback_module</c>
</tag>
<item>
<p>
@@ -1270,21 +1284,21 @@ handle_event(_, _, State, Data) ->
<p>
These <em>transition actions</em> can be invoked by
returning them from the
- <seealso marker="#state callback"><em>state callback</em></seealso>, from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>, from
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
or by giving them to
- <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
+ <seemfa marker="#enter_loop/5"><c>enter_loop/5,6</c></seemfa>.
</p>
<p>
Actions are executed in the containing list order.
</p>
<p>
Actions that set
- <seealso marker="#type-transition_option">transition options</seealso>
+ <seetype marker="#transition_option">transition options</seetype>
override any previous of the same type,
so the last in the containing list wins.
For example, the last
- <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
+ <seetype marker="#event_timeout"><c>event_timeout()</c></seetype>
overrides any previous <c>event_timeout()</c> in the list.
</p>
<taglist>
@@ -1292,8 +1306,8 @@ handle_event(_, _, State, Data) ->
<item>
<p>
Sets the
- <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>
- <seealso marker="#type-hibernate"><c>hibernate()</c></seealso>
+ <seetype marker="#transition_option"><c>transition_option()</c></seetype>
+ <seetype marker="#hibernate"><c>hibernate()</c></seetype>
for this <em>state transition</em>.
</p>
</item>
@@ -1306,14 +1320,14 @@ handle_event(_, _, State, Data) ->
<p>
These <em>transition actions</em> can be invoked by
returning them from the
- <seealso marker="#state callback"><em>state callback</em></seealso>, from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>, from
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
or by giving them to
- <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
+ <seemfa marker="#enter_loop/5"><c>enter_loop/5,6</c></seemfa>.
</p>
<p>
These time-out actions sets time-out
- <seealso marker="#type-transition_option">transition options</seealso>.
+ <seetype marker="#transition_option">transition options</seetype>.
</p>
<taglist>
<tag><c>Time</c></tag>
@@ -1322,7 +1336,7 @@ handle_event(_, _, State, Data) ->
Short for <c>{timeout,Time,Time}</c>, that is,
the time-out message is the time-out time.
This form exists to make the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
return value <c>{next_state,NextState,NewData,Time}</c>
allowed like for <c>gen_fsm</c>.
</p>
@@ -1331,34 +1345,34 @@ handle_event(_, _, State, Data) ->
<item>
<p>
Sets the
- <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>
- <seealso marker="#type-event_timeout"><c>event_timeout()</c></seealso>
+ <seetype marker="#transition_option"><c>transition_option()</c></seetype>
+ <seetype marker="#event_timeout"><c>event_timeout()</c></seetype>
to <c><anno>Time</anno></c> with <c><anno>EventContent</anno></c>
and time-out options
- <seealso marker="#type-timeout_option"><c><anno>Options</anno></c></seealso>.
+ <seetype marker="#timeout_option"><c><anno>Options</anno></c></seetype>.
</p>
</item>
<tag><c>{timeout,<anno>Name</anno>}</c></tag>
<item>
<p>
Sets the
- <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>
- <seealso marker="#type-generic_timeout"><c>generic_timeout()</c></seealso>
+ <seetype marker="#transition_option"><c>transition_option()</c></seetype>
+ <seetype marker="#generic_timeout"><c>generic_timeout()</c></seetype>
to <c><anno>Time</anno></c> for <c><anno>Name</anno></c>
with <c><anno>EventContent</anno></c>
and time-out options
- <seealso marker="#type-timeout_option"><c><anno>Options</anno></c></seealso>.
+ <seetype marker="#timeout_option"><c><anno>Options</anno></c></seetype>.
</p>
</item>
<tag><c>state_timeout</c></tag>
<item>
<p>
Sets the
- <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>
- <seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
+ <seetype marker="#transition_option"><c>transition_option()</c></seetype>
+ <seetype marker="#state_timeout"><c>state_timeout()</c></seetype>
to <c><anno>Time</anno></c> with <c><anno>EventContent</anno></c>
and time-out options
- <seealso marker="#type-timeout_option"><c><anno>Options</anno></c></seealso>.
+ <seetype marker="#timeout_option"><c><anno>Options</anno></c></seetype>.
</p>
</item>
</taglist>
@@ -1369,9 +1383,9 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
This is a shorter and clearer form of
- <seealso marker="#type-timeout_action">
+ <seetype marker="#timeout_action">
timeout_action()
- </seealso>
+ </seetype>
with <c>Time = infinity</c> which cancels a time-out.
</p>
</desc>
@@ -1382,9 +1396,9 @@ handle_event(_, _, State, Data) ->
<p>
Updates a time-out with a new <c>EventContent</c>.
See
- <seealso marker="#type-timeout_action">
+ <seetype marker="#timeout_action">
timeout_action()
- </seealso>
+ </seetype>
for how to start a time-out.
</p>
<p>
@@ -1400,31 +1414,31 @@ handle_event(_, _, State, Data) ->
<p>
This <em>transition action</em> can be invoked by
returning it from the
- <seealso marker="#state callback"><em>state callback</em></seealso>, from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>, from
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
or by giving it to
- <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
+ <seemfa marker="#enter_loop/5"><c>enter_loop/5,6</c></seemfa>.
</p>
<p>
It does not set any
- <seealso marker="#type-transition_option">
+ <seetype marker="#transition_option">
<c>transition_option()</c>
- </seealso>
+ </seetype>
but instead replies to a caller waiting for a reply in
- <seealso marker="#call/2"><c>call/2</c></seealso>.
+ <seemfa marker="#call/2"><c>call/2</c></seemfa>.
<c><anno>From</anno></c> must be the term from argument
- <seealso marker="#type-event_type"><c>{call,<anno>From</anno>}</c></seealso>
+ <seetype marker="#event_type"><c>{call,<anno>From</anno>}</c></seetype>
in a call to a
- <seealso marker="#state callback"><em>state callback</em></seealso>.
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>.
</p>
<p>
Note that using this action from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
or
- <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>
+ <seemfa marker="#enter_loop/5"><c>enter_loop/5,6</c></seemfa>
would be weird on the border of witchcraft
since there has been no earlier call to a
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
in this server.
</p>
</desc>
@@ -1435,16 +1449,16 @@ handle_event(_, _, State, Data) ->
<p>
For a succesful initialization,
<c><anno>State</anno></c> is the initial
- <seealso marker="#type-state"><c>state()</c></seealso>
+ <seetype marker="#state"><c>state()</c></seetype>
and <c><anno>Data</anno></c> the initial server
- <seealso marker="#type-data"><c>data()</c></seealso>
+ <seetype marker="#data"><c>data()</c></seetype>
of the <c>gen_statem</c>.
</p>
<p>
- The <seealso marker="#type-action"><c>Actions</c></seealso>
+ The <seetype marker="#action"><c>Actions</c></seetype>
are executed when entering the first
- <seealso marker="#type-state">state</seealso> just as for a
- <seealso marker="#state callback"><em>state callback</em></seealso>,
+ <seetype marker="#state">state</seetype> just as for a
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>,
except that the action <c>postpone</c> is forced to
<c>false</c> since there is no event to postpone.
</p>
@@ -1452,7 +1466,7 @@ handle_event(_, _, State, Data) ->
For an unsuccesful initialization,
<c>{stop,<anno>Reason</anno>}</c>
or <c>ignore</c> should be used; see
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
+ <seemfa marker="#start_link/3"><c>start_link/3,4</c></seemfa>.
</p>
</desc>
</datatype>
@@ -1463,7 +1477,7 @@ handle_event(_, _, State, Data) ->
<c><anno>State</anno></c> is the current state
and it cannot be changed since the state callback
was called with a
- <seealso marker="#type-state_enter"><em>state enter call</em></seealso>.
+ <seetype marker="#state_enter"><em>state enter call</em></seetype>.
</p>
<taglist>
<tag><c>next_state</c></tag>
@@ -1484,13 +1498,13 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
<c><anno>StateType</anno></c> is
- <seealso marker="#type-state_name"><c>state_name()</c></seealso>
+ <seetype marker="#state_name"><c>state_name()</c></seetype>
if
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
is <c>state_functions</c>, or
- <seealso marker="#type-state"><c>state()</c></seealso>
+ <seetype marker="#state"><c>state()</c></seetype>
if
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
is <c>handle_event_function</c>.
</p>
<taglist>
@@ -1514,11 +1528,11 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
<c><anno>ActionType</anno></c> is
- <seealso marker="#type-enter_action"><c>enter_action()</c></seealso>
+ <seetype marker="#enter_action"><c>enter_action()</c></seetype>
if the state callback was called with a
- <seealso marker="#type-state_enter"><em>state enter call</em></seealso>
+ <seetype marker="#state_enter"><em>state enter call</em></seetype>
and
- <seealso marker="#type-action"><c>action()</c></seealso>
+ <seetype marker="#action"><c>action()</c></seetype>
if the state callback was called with an event.
</p>
<taglist>
@@ -1540,9 +1554,9 @@ handle_event(_, _, State, Data) ->
<item>
<p>
If the <c>gen_statem</c> runs with
- <seealso marker="#type-state_enter"><em>state enter calls</em></seealso>,
+ <seetype marker="#state_enter"><em>state enter calls</em></seetype>,
the <em>state enter call</em> is repeated, see type
- <seealso marker="#type-transition_option"><c>transition_option()</c></seealso>,
+ <seetype marker="#transition_option"><c>transition_option()</c></seetype>,
other than that <c>repeat_state</c> is the same as
<c>keep_state</c>.
</p>
@@ -1558,7 +1572,7 @@ handle_event(_, _, State, Data) ->
<item>
<p>
Terminates the <c>gen_statem</c> by calling
- <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ <seemfa marker="#Module:terminate/3"><c>Module:terminate/3</c></seemfa>
with <c>Reason</c> and
<c><anno>NewData</anno></c>, if specified.
</p>
@@ -1568,7 +1582,7 @@ handle_event(_, _, State, Data) ->
<p>
Sends all <c><anno>Replies</anno></c>,
then terminates the <c>gen_statem</c> by calling
- <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ <seemfa marker="#Module:terminate/3"><c>Module:terminate/3</c></seemfa>
with <c>Reason</c> and
<c><anno>NewData</anno></c>, if specified.
</p>
@@ -1580,6 +1594,16 @@ handle_event(_, _, State, Data) ->
</p>
</desc>
</datatype>
+
+ <datatype>
+ <name name="request_id"/>
+ <desc>
+ <p>
+ A request handle, see <seemfa marker="#send_request/2"> <c>send_request/2</c> </seemfa>
+ for details.
+ </p>
+ </desc>
+ </datatype>
</datatypes>
<funcs>
@@ -1590,22 +1614,22 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Makes a synchronous call to the <c>gen_statem</c>
- <seealso marker="#type-server_ref"><c><anno>ServerRef</anno></c></seealso>
+ <seetype marker="#server_ref"><c><anno>ServerRef</anno></c></seetype>
by sending a request
and waiting until its reply arrives.
The <c>gen_statem</c> calls the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
with
- <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <seetype marker="#event_type"><c>event_type()</c></seetype>
<c>{call,From}</c> and event content
<c><anno>Request</anno></c>.
</p>
<p>
A <c><anno>Reply</anno></c> is generated when a
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
returns with
<c>{reply,From,<anno>Reply</anno>}</c> as one
- <seealso marker="#type-action"><c>action()</c></seealso>,
+ <seetype marker="#action"><c>action()</c></seetype>,
and that <c><anno>Reply</anno></c> becomes the return value
of this function.
</p>
@@ -1666,14 +1690,14 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Sends an asynchronous event to the <c>gen_statem</c>
- <seealso marker="#type-server_ref"><c><anno>ServerRef</anno></c></seealso>
+ <seetype marker="#server_ref"><c><anno>ServerRef</anno></c></seetype>
and returns <c>ok</c> immediately,
ignoring if the destination node or <c>gen_statem</c>
does not exist.
The <c>gen_statem</c> calls the
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
with
- <seealso marker="#type-event_type"><c>event_type()</c></seealso>
+ <seetype marker="#event_type"><c>event_type()</c></seetype>
<c>cast</c> and event content
<c><anno>Msg</anno></c>.
</p>
@@ -1681,14 +1705,45 @@ handle_event(_, _, State, Data) ->
</func>
<func>
+ <name name="check_response" arity="2" since="OTP-23"/>
+ <fsummary>Check if a message is a reply from a server.</fsummary>
+ <desc>
+ <p>
+ This function is used to check if a previously received
+ message, for example by <c>receive</c> or
+ <c>handle_info/2</c>, is a result of a request made with
+ <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>.
+ If <c>Msg</c> is a reply to the handle <c>RequestId</c>
+ the result of the request is returned in <c>Reply</c>.
+ Otherwise returns <c>no_reply</c> and no cleanup is done, and
+ thus the function shall be invoked repeatedly until a reply
+ is returned.
+ </p>
+ <p>
+ The return value <c><anno>Reply</anno></c> is generated when a
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
+ returns with
+ <c>{reply,From,<anno>Reply</anno>}</c> as one
+ <seetype marker="#action"><c>action()</c></seetype>,
+ and that <c><anno>Reply</anno></c> becomes the return value
+ of this function.
+ </p>
+ <p>
+ The function returns an error if the <c>gen_statem</c>
+ dies before or during this request.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name name="enter_loop" arity="4" since="OTP 19.1"/>
<fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>
<desc>
<p>
The same as
- <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
+ <seemfa marker="#enter_loop/6"><c>enter_loop/6</c></seemfa>
with <c>Actions = []</c> except that no
- <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ <seetype marker="#server_name"><c>server_name()</c></seetype>
must have been registered. This creates an anonymous server.
</p>
</desc>
@@ -1701,16 +1756,16 @@ handle_event(_, _, State, Data) ->
<p>
If <c><anno>Server_or_Actions</anno></c> is a <c>list()</c>,
the same as
- <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
+ <seemfa marker="#enter_loop/6"><c>enter_loop/6</c></seemfa>
except that no
- <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ <seetype marker="#server_name"><c>server_name()</c></seetype>
must have been registered and
<c>Actions = <anno>Server_or_Actions</anno></c>.
This creates an anonymous server.
</p>
<p>
Otherwise the same as
- <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
+ <seemfa marker="#enter_loop/6"><c>enter_loop/6</c></seemfa>
with
<c>Server = <anno>Server_or_Actions</anno></c> and
<c>Actions = []</c>.
@@ -1729,7 +1784,7 @@ handle_event(_, _, State, Data) ->
a <c>gen_statem</c> server.
The process <em>must</em> have been started
using one of the start functions in
- <seealso marker="proc_lib"><c>proc_lib</c></seealso>.
+ <seeerl marker="proc_lib"><c>proc_lib</c></seeerl>.
The user is responsible for any initialization of the process,
including registering a name for it.
</p>
@@ -1741,18 +1796,18 @@ handle_event(_, _, State, Data) ->
<p>
<c><anno>Module</anno></c>, <c><anno>Opts</anno></c>
have the same meaning as when calling
- <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>.
+ <seemfa marker="#start_link/3"><c>start[_link|_monitor]/3,4</c></seemfa>.
</p>
<p>
If <c><anno>Server</anno></c> is <c>self()</c> an anonymous
server is created just as when using
- <seealso marker="#start_link/3"><c>start[_link]/3</c></seealso>.
+ <seemfa marker="#start_link/3"><c>start[_link|_monitor]/3</c></seemfa>.
If <c><anno>Server</anno></c> is a
- <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ <seetype marker="#server_name"><c>server_name()</c></seetype>
a named server is created just as when using
- <seealso marker="#start_link/4"><c>start[_link]/4</c></seealso>.
+ <seemfa marker="#start_link/4"><c>start[_link|_monitor]/4</c></seemfa>.
However, the
- <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ <seetype marker="#server_name"><c>server_name()</c></seetype>
name must have been registered accordingly
<em>before</em> this function is called.
</p>
@@ -1760,17 +1815,17 @@ handle_event(_, _, State, Data) ->
<c><anno>State</anno></c>, <c><anno>Data</anno></c>,
and <c><anno>Actions</anno></c>
have the same meanings as in the return value of
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>.
Also, the callback module does not need to export a
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
function.
</p>
<p>
The function fails if the calling process was not started by a
- <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ <seeerl marker="proc_lib"><c>proc_lib</c></seeerl>
start function, or if it is not registered
according to
- <seealso marker="#type-server_name"><c>server_name()</c></seealso>.
+ <seetype marker="#server_name"><c>server_name()</c></seetype>.
</p>
</desc>
</func>
@@ -1783,32 +1838,74 @@ handle_event(_, _, State, Data) ->
<p>
This function can be used by a <c>gen_statem</c>
to explicitly send a reply to a process that waits in
- <seealso marker="#call/2"><c>call/2</c></seealso>
+ <seemfa marker="#call/2"><c>call/2</c></seemfa>
when the reply cannot be defined in
the return value of a
- <seealso marker="#state callback"><em>state callback</em></seealso>.
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>.
</p>
<p>
<c><anno>From</anno></c> must be the term from argument
- <seealso marker="#type-event_type"><c>{call,<anno>From</anno>}</c></seealso>
+ <seetype marker="#event_type"><c>{call,<anno>From</anno>}</c></seetype>
to the
- <seealso marker="#state callback"><em>state callback</em></seealso>.
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>.
A reply or multiple replies canalso be sent
using one or several
- <seealso marker="#type-reply_action"><c>reply_action()</c></seealso>s
+ <seetype marker="#reply_action"><c>reply_action()</c></seetype>s
from a
- <seealso marker="#state callback"><em>state callback</em></seealso>.
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>.
</p>
<note>
<p>
A reply sent with this function is not visible
- in <seealso marker="sys"><c>sys</c></seealso> debug output.
+ in <seeerl marker="sys"><c>sys</c></seeerl> debug output.
</p>
</note>
</desc>
</func>
<func>
+ <name name="send_request" arity="2" since="OTP-23"/>
+ <fsummary>Send a request to a <c>gen_statem</c>.</fsummary>
+ <desc>
+ <p>
+ Sends a request to the <c>gen_statem</c>
+ <seetype marker="#server_ref"><c><anno>ServerRef</anno></c></seetype>
+ and returns a handle <c><anno>RequestId</anno></c>.
+ </p>
+ <p>
+ The return value <c><anno>RequestId</anno></c> shall later be used with
+ <seemfa marker="#wait_response/2"> <c>wait_response/1,2</c></seemfa> or
+ <seemfa marker="#check_response/2"> <c>check_response/2</c></seemfa>
+ to fetch the actual result of the request.
+ </p>
+ <p>
+ The call <c>gen_statem:wait_response(gen_statem:send_request(ServerRef,Request), Timeout)</c>
+ can be seen as equivalent to
+ <seemfa marker="#call/3"><c>gen_statem:call(Server,Request,Timeout)</c></seemfa>,
+ ignoring the error handling.
+ </p>
+ <p>
+ The <c>gen_statem</c> calls the
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
+ with
+ <seetype marker="#event_type"><c>event_type()</c></seetype>
+ <c>{call,From}</c> and event content
+ <c><anno>Request</anno></c>.
+ </p>
+ <p>
+ A <c>Reply</c> is generated when a
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
+ returns with
+ <c>{reply,From,Reply}</c> as one
+ <seetype marker="#action"><c>action()</c></seetype>,
+ and that <c>Reply</c> becomes the return value
+ of <seemfa marker="#wait_response/2"> <c>wait_response/1,2</c></seemfa> or
+ <seemfa marker="#check_response/2"> <c>check_response/2</c></seemfa> function.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name name="start" arity="3" since="OTP 19.0"/>
<name name="start" arity="4" since="OTP 19.0"/>
<fsummary>Create a standalone <c>gen_statem</c> process.</fsummary>
@@ -1816,7 +1913,7 @@ handle_event(_, _, State, Data) ->
<p>
Creates a standalone <c>gen_statem</c> process according to
OTP design principles (using
- <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ <seeerl marker="proc_lib"><c>proc_lib</c></seeerl>
primitives).
As it does not get linked to the calling process,
this start function cannot be used by a supervisor
@@ -1824,7 +1921,7 @@ handle_event(_, _, State, Data) ->
</p>
<p>
For a description of arguments and return values, see
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
+ <seemfa marker="#start_link/3"><c>start_link/3,4</c></seemfa>.
</p>
</desc>
</func>
@@ -1838,7 +1935,7 @@ handle_event(_, _, State, Data) ->
Creates a <c>gen_statem</c> process according
to OTP design principles
(using
- <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ <seeerl marker="proc_lib"><c>proc_lib</c></seeerl>
primitives)
that is linked to the calling process.
This is essential when the <c>gen_statem</c> must be part of
@@ -1846,15 +1943,15 @@ handle_event(_, _, State, Data) ->
</p>
<p>
The <c>gen_statem</c> process calls
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
to initialize the server. To ensure a synchronized startup
procedure, <c>start_link/3,4</c> does not return until
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
has returned.
</p>
<p>
<c><anno>ServerName</anno></c> specifies the
- <seealso marker="#type-server_name"><c>server_name()</c></seealso>
+ <seetype marker="#server_name"><c>server_name()</c></seetype>
to register for the <c>gen_statem</c>.
If the <c>gen_statem</c> is started with <c>start_link/3</c>,
no <c><anno>ServerName</anno></c> is provided and
@@ -1864,53 +1961,53 @@ handle_event(_, _, State, Data) ->
<p>
<c><anno>Args</anno></c> is an arbitrary term that is passed as
the argument to
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>.
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>.
</p>
<list type="bulleted">
<item>
<p>
If option
- <seealso marker="#type-start_opt">
+ <seetype marker="#start_opt">
<c>{timeout,Time}</c>
- </seealso>
+ </seetype>
is present in
<c><anno>Opts</anno></c>, the <c>gen_statem</c>
is allowed to spend <c>Time</c> milliseconds initializing
or it terminates and the start function returns
- <seealso marker="#type-start_ret"><c>{error,timeout}</c></seealso>.
+ <seetype marker="#start_ret"><c>{error,timeout}</c></seetype>.
</p>
</item>
<item>
<p>If option
- <seealso marker="#type-enter_loop_opt">
+ <seetype marker="#enter_loop_opt">
<c>{hibernate_after,HibernateAfterTimeout}</c>
- </seealso>
+ </seetype>
is present, the <c>gen_statem</c>
process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and
if no message is received, the process goes into hibernation automatically
- (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).
+ (by calling <seemfa marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seemfa>).
</p>
</item>
<item>
<p>
If option
- <seealso marker="#type-enter_loop_opt">
+ <seetype marker="#enter_loop_opt">
<c>{debug,Dbgs}</c>
- </seealso>
+ </seetype>
is present in <c><anno>Opts</anno></c>, debugging through
- <seealso marker="sys"><c>sys</c></seealso> is activated.
+ <seeerl marker="sys"><c>sys</c></seeerl> is activated.
</p>
</item>
<item>
<p>
If option
- <seealso marker="#type-start_opt">
+ <seetype marker="#start_opt">
<c>{spawn_opt,SpawnOpts}</c>
- </seealso>
+ </seetype>
is present in
<c><anno>Opts</anno></c>, <c>SpawnOpts</c> is passed
as option list to
- <seealso marker="erts:erlang#spawn_opt/2"><c>erlang:spawn_opt/2</c></seealso>,
+ <seemfa marker="erts:erlang#spawn_opt/2"><c>erlang:spawn_opt/2</c></seemfa>,
which is used to spawn the <c>gen_statem</c> process.
</p>
</item>
@@ -1925,39 +2022,68 @@ handle_event(_, _, State, Data) ->
<p>
If the <c>gen_statem</c> is successfully created
and initialized, this function returns
- <seealso marker="#type-start_ret"><c>{ok,Pid}</c></seealso>,
+ <seetype marker="#start_ret"><c>{ok,Pid}</c></seetype>,
where <c>Pid</c> is the <c>pid()</c>
of the <c>gen_statem</c>.
If a process with the specified <c><anno>ServerName</anno></c>
exists already, this function returns
- <seealso marker="#type-start_ret"><c>{error,{already_started,Pid}}</c></seealso>,
+ <seetype marker="#start_ret"><c>{error,{already_started,Pid}}</c></seetype>,
where <c>Pid</c> is the <c>pid()</c> of that process.
</p>
<p>
If <c>Module:init/1</c> fails with <c>Reason</c>,
this function returns
- <seealso marker="#type-start_ret"><c>{error,Reason}</c></seealso>.
+ <seetype marker="#start_ret"><c>{error,Reason}</c></seetype>.
If <c>Module:init/1</c> returns
- <seealso marker="#type-start_ret"><c>{stop,Reason}</c></seealso>
+ <seetype marker="#start_ret"><c>{stop,Reason}</c></seetype>
or
- <seealso marker="#type-start_ret"><c>ignore</c></seealso>,
+ <seetype marker="#start_ret"><c>ignore</c></seetype>,
the process is terminated and this function
returns
- <seealso marker="#type-start_ret"><c>{error,Reason}</c></seealso>
+ <seetype marker="#start_ret"><c>{error,Reason}</c></seetype>
or
- <seealso marker="#type-start_ret"><c>ignore</c></seealso>,
+ <seetype marker="#start_ret"><c>ignore</c></seetype>,
respectively.
</p>
</desc>
</func>
<func>
+ <name name="start_monitor" arity="3" since="OTP 23.0"/>
+ <name name="start_monitor" arity="4" since="OTP 23.0"/>
+ <fsummary>Create a standalone <c>gen_statem</c> process.</fsummary>
+ <desc>
+ <p>
+ Creates a standalone <c>gen_statem</c> process according to
+ OTP design principles (using
+ <seeerl marker="proc_lib"><c>proc_lib</c></seeerl>
+ primitives) and atomically sets up a monitor to
+ the newly created process.
+ As it does not get linked to the calling process,
+ this start function cannot be used by a supervisor
+ to start a child.
+ </p>
+ <p>
+ For a description of arguments and return values, see
+ <seemfa marker="#start_link/3"><c>start_link/3,4</c></seemfa>.
+ Note that the return value on successful start differs from
+ <c>start_link/3,4</c>. <c>start_monitor/3,4</c> will return
+ <c>{ok,{Pid,Mon}}</c> where <c>Pid</c> is the process identifier
+ of the process, and <c>Mon</c> is a reference to the monitor
+ set up to monitor the process. If the start is not successful,
+ the caller will be blocked until the <c>DOWN</c> message has
+ been received and removed from the message queue.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name name="stop" arity="1" since="OTP 19.0"/>
<fsummary>Synchronously stop a generic server.</fsummary>
<desc>
<p>
The same as
- <seealso marker="#stop/3"><c>stop(<anno>ServerRef</anno>, normal, infinity)</c></seealso>.
+ <seemfa marker="#stop/3"><c>stop(<anno>ServerRef</anno>, normal, infinity)</c></seemfa>.
</p>
</desc>
</func>
@@ -1968,11 +2094,11 @@ handle_event(_, _, State, Data) ->
<desc>
<p>
Orders the <c>gen_statem</c>
- <seealso marker="#type-server_ref"><c><anno>ServerRef</anno></c></seealso>
+ <seetype marker="#server_ref"><c><anno>ServerRef</anno></c></seetype>
to exit with the specified <c><anno>Reason</anno></c>
and waits for it to terminate.
The <c>gen_statem</c> calls
- <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso>
+ <seemfa marker="#Module:terminate/3"><c>Module:terminate/3</c></seemfa>
before exiting.
</p>
<p>
@@ -1980,7 +2106,7 @@ handle_event(_, _, State, Data) ->
with the expected reason. Any other reason than <c>normal</c>,
<c>shutdown</c>, or <c>{shutdown,Term}</c> causes an
error report to be issued through
- <seealso marker="kernel:logger"><c>logger(3)</c></seealso>.
+ <seeerl marker="kernel:logger"><c>logger(3)</c></seeerl>.
The default <c><anno>Reason</anno></c> is <c>normal</c>.
</p>
<p>
@@ -1997,17 +2123,57 @@ handle_event(_, _, State, Data) ->
</p>
</desc>
</func>
+
+ <func>
+ <name name="wait_response" arity="1" since="OTP-23"/>
+ <name name="wait_response" arity="2" since="OTP-23"/>
+ <fsummary>Wait for a reply from a server.</fsummary>
+ <desc>
+ <p>
+ This function is used to wait for a reply of a request made with
+ <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
+ from the <c>gen_statem</c> process. This function must be called
+ from the same process from which
+ <seemfa marker="#send_request/2"><c>send_request/2</c></seemfa>
+ was made.
+ </p>
+ <p>
+ <c>Timeout</c> is an integer greater then or equal to zero
+ that specifies how many milliseconds to wait for an reply, or
+ the atom <c>infinity</c> to wait indefinitely. Defaults to
+ <c>infinity</c>.
+ If no reply is received within the specified
+ time, the function returns <c>timeout</c> and no cleanup is
+ done, and thus the function can be invoked repeatedly until a
+ reply is returned.
+ </p>
+ <p>
+ The return value <c><anno>Reply</anno></c> is generated when a
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
+ returns with
+ <c>{reply,From,<anno>Reply</anno>}</c> as one
+ <seetype marker="#action"><c>action()</c></seetype>,
+ and that <c><anno>Reply</anno></c> becomes the return value
+ of this function.
+ </p>
+ <p>
+ The function returns an error if the <c>gen_statem</c>
+ dies before or during this function call.
+ </p>
+ </desc>
+ </func>
</funcs>
- <section>
- <title>Callback Functions</title>
- <p>
- The following functions are to be exported from a
- <c>gen_statem</c> callback module.
- </p>
- </section>
+
<funcs>
+ <fsdescription>
+ <title>Callback Functions</title>
+ <p>
+ The following functions are to be exported from a
+ <c>gen_statem</c> callback module.
+ </p>
+ </fsdescription>
<func>
<name since="OTP 19.1">Module:callback_mode() -> CallbackMode</name>
<fsummary>
@@ -2016,49 +2182,49 @@ handle_event(_, _, State, Data) ->
<type>
<v>
CallbackMode =
- <seealso marker="#type-callback_mode">callback_mode()</seealso> |
- [ <seealso marker="#type-callback_mode">callback_mode()</seealso>
- | <seealso marker="#type-state_enter">state_enter()</seealso> ]
+ <seetype marker="#callback_mode">callback_mode()</seetype> |
+ [ <seetype marker="#callback_mode">callback_mode()</seetype>
+ | <seetype marker="#state_enter">state_enter()</seetype> ]
</v>
</type>
<desc>
<p>
This function is called by a <c>gen_statem</c>
when it needs to find out the
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
of the callback module. The value is cached by <c>gen_statem</c>
for efficiency reasons, so this function is only called
once after server start, after code change,
and after changing the callback module,
but before the first
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
in the current callback module's code version is called.
More occasions may be added in future versions
of <c>gen_statem</c>.
</p>
<p>
Server start happens either when
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
returns or when
- <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>
+ <seemfa marker="#enter_loop/4"><c>enter_loop/4-6</c></seemfa>
is called.
Code change happens when
- <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>
+ <seemfa marker="#Module:code_change/4"><c>Module:code_change/4</c></seemfa>
returns.
A change of the callback module happens when a
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
returns any of the actions
- <seealso marker="#type-action"><c>change_callback_module</c></seealso>,
- <seealso marker="#type-action"><c>push_callback_module</c></seealso> or
- <seealso marker="#type-action"><c>pop_callback_module</c></seealso>.
+ <seetype marker="#action"><c>change_callback_module</c></seetype>,
+ <seetype marker="#action"><c>push_callback_module</c></seetype> or
+ <seetype marker="#action"><c>pop_callback_module</c></seetype>.
</p>
<p>
The <c>CallbackMode</c> is either just
- <seealso marker="#type-callback_mode"><c>callback_mode()</c></seealso>
+ <seetype marker="#callback_mode"><c>callback_mode()</c></seetype>
or a list containing
- <seealso marker="#type-callback_mode"><c>callback_mode()</c></seealso>
+ <seetype marker="#callback_mode"><c>callback_mode()</c></seetype>
and possibly the atom
- <seealso marker="#type-state_enter"><c>state_enter</c></seealso>.
+ <seetype marker="#state_enter"><c>state_enter</c></seetype>.
</p>
<note>
<p>
@@ -2084,11 +2250,11 @@ handle_event(_, _, State, Data) ->
<v>Result = {ok,NewState,NewData} | Reason</v>
<v>
OldState = NewState =
- <seealso marker="#type-state">state()</seealso>
+ <seetype marker="#state">state()</seetype>
</v>
<v>
OldData = NewData =
- <seealso marker="#type-data">data()</seealso>
+ <seetype marker="#data">data()</seetype>
</v>
<v>Reason = term()</v>
</type>
@@ -2108,9 +2274,9 @@ handle_event(_, _, State, Data) ->
update its internal state during a release upgrade/downgrade,
that is, when the instruction <c>{update,Module,Change,...}</c>,
where <c>Change = {advanced,Extra}</c>, is specified in the
- <seealso marker="sasl:appup"><c>appup</c></seealso>
+ <seefile marker="sasl:appup"><c>appup</c></seefile>
file. For more information, see
- <seealso marker="doc/design_principles:release_handling#instr">OTP Design Principles</seealso>.
+ <seeguide marker="system/design_principles:release_handling#instr">OTP Design Principles</seeguide>.
</p>
<p>
For an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and
@@ -2148,20 +2314,20 @@ handle_event(_, _, State, Data) ->
Also note when upgrading a <c>gen_statem</c>,
this function and hence
the <c>Change = {advanced,Extra}</c> parameter in the
- <seealso marker="sasl:appup"><c>appup</c></seealso> file
+ <seefile marker="sasl:appup"><c>appup</c></seefile> file
is not only needed to update the internal state
or to act on the <c>Extra</c> argument.
It is also needed if an upgrade or downgrade should change
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>,
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>,
or else the <em>callback mode</em> after the code change
will not be honoured,
most probably causing a server crash.
</p>
<p>
If the server changes callback module using any of the actions
- <seealso marker="#type-action"><c>change_callback_module</c></seealso>,
- <seealso marker="#type-action"><c>push_callback_module</c></seealso> or
- <seealso marker="#type-action"><c>pop_callback_module</c></seealso>,
+ <seetype marker="#action"><c>change_callback_module</c></seetype>,
+ <seetype marker="#action"><c>push_callback_module</c></seetype> or
+ <seetype marker="#action"><c>pop_callback_module</c></seetype>,
be aware that it is always the current callback module that
will get this callback call. That the current callback module
handles the current state and data update should be no surprise,
@@ -2171,7 +2337,7 @@ handle_event(_, _, State, Data) ->
</p>
<p>
In the supervisor
- <seealso marker="doc/design_principles:sup_princ#child-specification">child specification</seealso>
+ <seeguide marker="system/design_principles:sup_princ#child-specification">child specification</seeguide>
there is a list of modules which is recommended to contain
only the callback module.
For a <c>gen_statem</c> with multiple callback modules
@@ -2200,16 +2366,17 @@ handle_event(_, _, State, Data) ->
<v>Args = term()</v>
<v>
Result(StateType) =
- <seealso marker="#type-init_result">init_result(StateType)</seealso>
+ <seetype marker="#init_result">init_result(StateType)</seetype>
</v>
</type>
<desc>
<marker id="Module:init-1"/>
<p>
Whenever a <c>gen_statem</c> is started using
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>
+ <seemfa marker="#start_link/3"><c>start_link/3,4</c></seemfa>,
+ <seemfa marker="#start_monitor/3"><c>start_monitor/3,4</c></seemfa>,
or
- <seealso marker="#start/3"><c>start/3,4</c></seealso>,
+ <seemfa marker="#start/3"><c>start/3,4</c></seemfa>,
this function is called by the new process to initialize
the implementation state and server data.
</p>
@@ -2220,9 +2387,9 @@ handle_event(_, _, State, Data) ->
<note>
<p>
Note that if the <c>gen_statem</c> is started through
- <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ <seeerl marker="proc_lib"><c>proc_lib</c></seeerl>
and
- <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>,
+ <seemfa marker="#enter_loop/4"><c>enter_loop/4-6</c></seemfa>,
this callback will never be called.
Since this callback is not optional it can
in that case be implemented as:
@@ -2245,11 +2412,11 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
<v>PDict = [{Key, Value}]</v>
<v>
State =
- <seealso marker="#type-state">state()</seealso>
+ <seetype marker="#state">state()</seetype>
</v>
<v>
Data =
- <seealso marker="#type-data">data()</seealso>
+ <seetype marker="#data">data()</seetype>
</v>
<v>Key = term()</v>
<v>Value = term()</v>
@@ -2276,7 +2443,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
<list type="bulleted">
<item>
One of
- <seealso marker="sys#get_status/1"><c>sys:get_status/1,2</c></seealso>
+ <seemfa marker="sys#get_status/1"><c>sys:get_status/1,2</c></seemfa>
is invoked to get the <c>gen_statem</c> status. <c>Opt</c> is set
to the atom <c>normal</c> for this case.
</item>
@@ -2289,7 +2456,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
This function is useful for changing the form and
appearance of the <c>gen_statem</c> status for these cases. A
callback module wishing to change the
- <seealso marker="sys#get_status/1"><c>sys:get_status/1,2</c></seealso>
+ <seemfa marker="sys#get_status/1"><c>sys:get_status/1,2</c></seemfa>
return value and how
its status appears in termination error logs exports an
instance of <c>format_status/2</c>, which returns a term
@@ -2300,11 +2467,11 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
of the <c>gen_statem</c>.
</p>
<p>
- <seealso marker="#type-state"><c>State</c></seealso>
+ <seetype marker="#state"><c>State</c></seetype>
is the internal state of the <c>gen_statem</c>.
</p>
<p>
- <seealso marker="#type-data"><c>Data</c></seealso>
+ <seetype marker="#data"><c>Data</c></seetype>
is the internal server data of the <c>gen_statem</c>.
</p>
<p>
@@ -2313,7 +2480,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
of the current state and status of
the <c>gen_statem</c>. There are no restrictions on the
form <c>Status</c> can take, but for the
- <seealso marker="sys#get_status/1"><c>sys:get_status/1,2</c></seealso>
+ <seemfa marker="sys#get_status/1"><c>sys:get_status/1,2</c></seemfa>
case (when <c>Opt</c>
is <c>normal</c>), the recommended form for
the <c>Status</c> value is <c>[{data, [{"State",
@@ -2321,7 +2488,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
the <c>gen_statem</c> state. Following this recommendation is not
required, but it makes the callback module status
consistent with the rest of the
- <seealso marker="sys#get_status/1"><c>sys:get_status/1,2</c></seealso>
+ <seemfa marker="sys#get_status/1"><c>sys:get_status/1,2</c></seemfa>
return value.
</p>
<p>
@@ -2350,56 +2517,56 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
<type>
<v>
EventType =
- <seealso marker="#type-event_type">event_type()</seealso>
+ <seetype marker="#event_type">event_type()</seetype>
</v>
<v>EventContent = term()</v>
<v>
State =
- <seealso marker="#type-state">state()</seealso>
+ <seetype marker="#state">state()</seetype>
</v>
<v>
Data = NewData =
- <seealso marker="#type-data">data()</seealso>
+ <seetype marker="#data">data()</seetype>
</v>
<v>
StateEnterResult(StateName) =
- <seealso marker="#type-state_enter_result">state_enter_result(StateName)</seealso>
+ <seetype marker="#state_enter_result">state_enter_result(StateName)</seetype>
</v>
<v>
StateFunctionResult =
- <seealso marker="#type-event_handler_result">event_handler_result</seealso>(<seealso marker="#type-state_name">state_name()</seealso>)
+ <seetype marker="#event_handler_result">event_handler_result</seetype>(<seetype marker="#state_name">state_name()</seetype>)
</v>
<v>
StateEnterResult(State) =
- <seealso marker="#type-state_enter_result">state_enter_result(State)</seealso>
+ <seetype marker="#state_enter_result">state_enter_result(State)</seetype>
</v>
<v>
HandleEventResult =
- <seealso marker="#type-event_handler_result">event_handler_result</seealso>(<seealso marker="#type-state">state()</seealso>)
+ <seetype marker="#event_handler_result">event_handler_result</seetype>(<seetype marker="#state">state()</seetype>)
</v>
</type>
<desc>
<p>
Whenever a <c>gen_statem</c> receives an event from
- <seealso marker="#call/2"><c>call/2</c></seealso>,
- <seealso marker="#cast/2"><c>cast/2</c></seealso>, or
+ <seemfa marker="#call/2"><c>call/2</c></seemfa>,
+ <seemfa marker="#cast/2"><c>cast/2</c></seemfa>, or
as a normal process message, one of these functions is called. If
- <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>
+ <seetype marker="#callback_mode"><em>callback mode</em></seetype>
is <c>state_functions</c>, <c>Module:StateName/3</c> is called,
and if it is <c>handle_event_function</c>,
<c>Module:handle_event/4</c> is called.
</p>
<p>
If <c>EventType</c> is
- <seealso marker="#type-event_type"><c>{call,From}</c></seealso>,
+ <seetype marker="#event_type"><c>{call,From}</c></seetype>,
the caller waits for a reply. The reply can be sent
from this or from any other
- <seealso marker="#state callback"><em>state callback</em></seealso>
+ <seeerl marker="#state callback"><em>state callback</em></seeerl>
by returning with <c>{reply,From,Reply}</c> in
- <seealso marker="#type-action"><c>Actions</c></seealso>, in
- <seealso marker="#type-reply_action"><c>Replies</c></seealso>,
+ <seetype marker="#action"><c>Actions</c></seetype>, in
+ <seetype marker="#reply_action"><c>Replies</c></seetype>,
or by calling
- <seealso marker="#reply/2"><c>reply(From, Reply)</c></seealso>.
+ <seemfa marker="#reply/2"><c>reply(From, Reply)</c></seemfa>.
</p>
<p>
If this function returns with a next state that
@@ -2415,20 +2582,20 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
<p>
For options that can be set and actions that can be done
by <c>gen_statem</c> after returning from this function,
- see <seealso marker="#type-action"><c>action()</c></seealso>.
+ see <seetype marker="#action"><c>action()</c></seetype>.
</p>
<p>
When the <c>gen_statem</c> runs with
- <seealso marker="#type-state_enter"><em>state enter calls</em></seealso>,
+ <seetype marker="#state_enter"><em>state enter calls</em></seetype>,
these functions are also called with arguments
<c>(enter, OldState, ...)</c> during every <em>state change</em>.
In this case there are some restrictions on the
- <seealso marker="#type-enter_action">actions</seealso>
+ <seetype marker="#enter_action">actions</seetype>
that may be returned:
- <seealso marker="#type-postpone"><c>postpone()</c></seealso>
+ <seetype marker="#postpone"><c>postpone()</c></seetype>
is not allowed since a <em>state enter call</em> is not
an event so there is no event to postpone, and
- <seealso marker="#type-action"><c>{next_event,_,_}</c></seealso>
+ <seetype marker="#action"><c>{next_event,_,_}</c></seetype>
is not allowed since using <em>state enter calls</em>
should not affect how events are consumed and produced.
You may also not change states from this call.
@@ -2451,7 +2618,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
</p>
<p>
Note the fact that you can use
- <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>
+ <seemfa marker="erts:erlang#throw/1"><c>throw</c></seemfa>
to return the result, which can be useful.
For example to bail out with <c>throw(keep_state_and_data)</c>
from deep within complex code that cannot
@@ -2466,8 +2633,8 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
<fsummary>Clean up before termination.</fsummary>
<type>
<v>Reason = normal | shutdown | {shutdown,term()} | term()</v>
- <v>State = <seealso marker="#type-state">state()</seealso></v>
- <v>Data = <seealso marker="#type-data">data()</seealso></v>
+ <v>State = <seetype marker="#state">state()</seetype></v>
+ <v>Data = <seetype marker="#data">data()</seetype></v>
<v>Ignored = term()</v>
</type>
<desc>
@@ -2479,13 +2646,13 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
<p>
This function is called by a <c>gen_statem</c>
when it is about to terminate. It is to be the opposite of
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ <seemfa marker="#Module:init/1"><c>Module:init/1</c></seemfa>
and do any necessary cleaning up. When it returns,
the <c>gen_statem</c> terminates with <c>Reason</c>. The return
value is ignored.</p>
<p>
<c>Reason</c> is a term denoting the stop reason and
- <seealso marker="#type-state"><c>State</c></seealso>
+ <seetype marker="#state"><c>State</c></seetype>
is the internal state of the <c>gen_statem</c>.
</p>
<p>
@@ -2493,7 +2660,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
is terminating.
If it is because another callback function has returned, a
stop tuple <c>{stop,Reason}</c> in
- <seealso marker="#type-action"><c>Actions</c></seealso>,
+ <seetype marker="#action"><c>Actions</c></seetype>,
<c>Reason</c> has the value specified in that tuple.
If it is because of a failure, <c>Reason</c> is the error reason.
</p>
@@ -2532,7 +2699,7 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
<c>shutdown</c>, or <c>{shutdown,Term}</c>,
the <c>gen_statem</c> is assumed to terminate because of an error
and an error report is issued using
- <seealso marker="kernel:logger"><c>logger(3)</c></seealso>.
+ <seeerl marker="kernel:logger"><c>logger(3)</c></seeerl>.
</p>
</desc>
</func>
@@ -2541,12 +2708,12 @@ init(Args) -> erlang:error(not_implemented, [Args]).</pre>
<section>
<title>See Also</title>
<p>
- <seealso marker="gen_event"><c>gen_event(3)</c></seealso>,
- <seealso marker="gen_fsm"><c>gen_fsm(3)</c></seealso>,
- <seealso marker="gen_server"><c>gen_server(3)</c></seealso>,
- <seealso marker="proc_lib"><c>proc_lib(3)</c></seealso>,
- <seealso marker="supervisor"><c>supervisor(3)</c></seealso>,
- <seealso marker="sys"><c>sys(3)</c></seealso>.
+ <seeerl marker="gen_event"><c>gen_event(3)</c></seeerl>,
+ <seeerl marker="gen_fsm"><c>gen_fsm(3)</c></seeerl>,
+ <seeerl marker="gen_server"><c>gen_server(3)</c></seeerl>,
+ <seeerl marker="proc_lib"><c>proc_lib(3)</c></seeerl>,
+ <seeerl marker="supervisor"><c>supervisor(3)</c></seeerl>,
+ <seeerl marker="sys"><c>sys(3)</c></seeerl>.
</p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
index d69e808586..9fd87d9cf1 100644
--- a/lib/stdlib/doc/src/io.xml
+++ b/lib/stdlib/doc/src/io.xml
@@ -39,29 +39,29 @@
parameter <c>IoDevice</c>. If included, it must be the pid of a
process that handles the I/O protocols. Normally, it is the
<c>IoDevice</c> returned by
- <seealso marker="kernel:file#open/2"><c>file:open/2</c></seealso>.</p>
+ <seemfa marker="kernel:file#open/2"><c>file:open/2</c></seemfa>.</p>
<p>For a description of the I/O protocols, see section
- <seealso marker="io_protocol">The Erlang I/O Protocol</seealso>
+ <seeguide marker="io_protocol">The Erlang I/O Protocol</seeguide>
in the User's Guide.</p>
<warning>
<p>As from Erlang/OTP R13A, data supplied to function
- <seealso marker="#put_chars/2"><c>put_chars/2</c></seealso>
- is to be in the <seealso marker="unicode#type-chardata">
- <c>unicode:chardata()</c></seealso> format. This means that programs
+ <seemfa marker="#put_chars/2"><c>put_chars/2</c></seemfa>
+ is to be in the <seetype marker="unicode#chardata">
+ <c>unicode:chardata()</c></seetype> format. This means that programs
supplying binaries to this function must convert them to UTF-8
before trying to output the data on an I/O device.</p>
<p>If an I/O device is set in binary mode, functions
- <seealso marker="#get_chars/2"><c>get_chars/2,3</c></seealso> and
- <seealso marker="#get_line/1"><c>get_line/1,2</c></seealso>
+ <seemfa marker="#get_chars/2"><c>get_chars/2,3</c></seemfa> and
+ <seemfa marker="#get_line/1"><c>get_line/1,2</c></seemfa>
can return binaries instead of lists.
The binaries are, as from Erlang/OTP R13A,
encoded in UTF-8.</p>
<p>To work with binaries in ISO Latin-1 encoding, use the
- <seealso marker="kernel:file"><c>file</c></seealso> module instead.</p>
+ <seeerl marker="kernel:file"><c>file</c></seeerl> module instead.</p>
<p>For conversion functions between character encodings, see the
- <seealso marker="stdlib:unicode"><c>unicode</c></seealso> module.</p>
+ <seeerl marker="stdlib:unicode"><c>unicode</c></seeerl> module.</p>
</warning>
</description>
@@ -71,7 +71,7 @@
<desc>
<p>An I/O device, either <c>standard_io</c>, <c>standard_error</c>, a
registered name, or a pid handling I/O protocols (returned from
- <seealso marker="kernel:file#open/2"><c>file:open/2</c></seealso>).
+ <seemfa marker="kernel:file#open/2"><c>file:open/2</c></seemfa>).
</p>
</desc>
</datatype>
@@ -317,8 +317,8 @@ ok</pre>
It defaults to 80. The precision specifies the initial
indentation of the term. It defaults to the number of
characters printed on this line in the <em>same</em> call to
- <seealso marker="#write/1"><c>write/1</c></seealso> or
- <seealso marker="#format/1"><c>format/1,2,3</c></seealso>.
+ <seemfa marker="#write/1"><c>write/1</c></seemfa> or
+ <seemfa marker="#format/1"><c>format/1,2,3</c></seemfa>.
For example, using <c>T</c> above:</p>
<pre>
4> <input>io:fwrite("Here T = ~62p~n", [T]).</input>
@@ -368,9 +368,9 @@ ok
ok</pre>
<p>By default, Erlang only detects lists of characters
in the Latin-1 range as strings, but the <c>+pc unicode</c>
- flag can be used to change this (see <seealso
+ flag can be used to change this (see <seemfa
marker="#printable_range/0">
- <c>printable_range/0</c></seealso> for details). For example:</p>
+ <c>printable_range/0</c></seemfa> for details). For example:</p>
<pre>
10> <input>io:fwrite("~p~n",[[214]]).</input>
"Ö"
@@ -804,8 +804,8 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in
<c><anno>Prompt</anno></c>. Starts reading at location
<c><anno>StartLocation</anno></c> (<c>1</c>). Argument
<c><anno>Options</anno></c> is passed on as argument
- <c>Options</c> of function <seealso marker="erl_scan#tokens/4">
- <c>erl_scan:tokens/4</c></seealso>. The data is tokenized and parsed
+ <c>Options</c> of function <seemfa marker="erl_scan#tokens/4">
+ <c>erl_scan:tokens/4</c></seemfa>. The data is tokenized and parsed
as if it was a sequence of Erlang expressions until a final dot
(<c>.</c>) is reached.</p>
<p>The function returns:</p>
@@ -856,8 +856,8 @@ enter><input>abc("hey".</input>
prompting it with <c><anno>Prompt</anno></c>. Starts reading at
location <c><anno>StartLocation</anno></c> (<c>1</c>). Argument
<c><anno>Options</anno></c> is passed on as argument
- <c>Options</c> of function <seealso marker="erl_scan#tokens/4">
- <c>erl_scan:tokens/4</c></seealso>. The data is tokenized and parsed
+ <c>Options</c> of function <seemfa marker="erl_scan#tokens/4">
+ <c>erl_scan:tokens/4</c></seemfa>. The data is tokenized and parsed
as if it was an Erlang form (one of the valid Erlang expressions
in an Erlang source file) until a final dot (<c>.</c>) is reached.</p>
<p>The function returns:</p>
@@ -905,15 +905,15 @@ enter><input>abc("hey".</input>
<p>By default, Erlang is started so that only the <c>latin1</c> range
of characters indicate that a list of integers is a string.</p>
<p>The simplest way to use the setting is to call
- <seealso marker="io_lib#printable_list/1">
- <c>io_lib:printable_list/1</c></seealso>, which uses the return
+ <seemfa marker="io_lib#printable_list/1">
+ <c>io_lib:printable_list/1</c></seemfa>, which uses the return
value of this function to decide if a list is a string of printable
characters.</p>
<note>
<p>In a future release, this function may return more values and
ranges. To avoid compatibility problems, it is recommended to use
- function <seealso marker="io_lib#printable_list/1">
- <c>io_lib:printable_list/1</c></seealso>.</p></note>
+ function <seemfa marker="io_lib#printable_list/1">
+ <c>io_lib:printable_list/1</c></seemfa>.</p></note>
</desc>
</func>
@@ -970,8 +970,8 @@ enter><input>abc("hey".</input>
with <c><anno>Prompt</anno></c>. Reading starts at location
<c><anno>StartLocation</anno></c>. Argument
<c><anno>Options</anno></c> is passed on as argument <c>Options</c>
- of function <seealso marker="erl_scan#tokens/4">
- <c>erl_scan:tokens/4</c></seealso>.</p>
+ of function <seemfa marker="erl_scan#tokens/4">
+ <c>erl_scan:tokens/4</c></seemfa>.</p>
<p>The function returns:</p>
<taglist>
<tag><c>{ok, Term, <anno>EndLocation</anno>}</c></tag>
@@ -1020,8 +1020,8 @@ enter><input>abc("hey".</input>
prompting it with <c>Prompt</c>. Reading starts at location
<c>StartLocation</c> (<c>1</c>). Argument <c><anno>Options</anno></c>
is passed on as argument <c>Options</c> of function
- <seealso marker="erl_scan#tokens/4">
- <c>erl_scan:tokens/4</c></seealso>. The data is tokenized as if it
+ <seemfa marker="erl_scan#tokens/4">
+ <c>erl_scan:tokens/4</c></seemfa>. The data is tokenized as if it
were a sequence of Erlang expressions until a final dot (<c>.</c>) is
reached. This token is also returned.</p>
<p>The function returns:</p>
@@ -1071,14 +1071,14 @@ enter><input>1.0er.</input>
prompting it with <c><anno>Prompt</anno></c>. Starts reading
at location <c><anno>StartLocation</anno></c> (<c>1</c>).
Argument <c><anno>Options</anno></c> is passed on as argument
- <c>Options</c> of function <seealso marker="erl_scan#tokens/4">
- <c>erl_scan:tokens/4</c></seealso>. The data is tokenized as if it
+ <c>Options</c> of function <seemfa marker="erl_scan#tokens/4">
+ <c>erl_scan:tokens/4</c></seemfa>. The data is tokenized as if it
was an Erlang form (one of the valid Erlang expressions in an
Erlang source file) until a final dot (<c>.</c>) is reached.
This last token is also returned.</p>
<p>The return values are the same as for
- <seealso marker="#scan_erl_exprs/1">
- <c>scan_erl_exprs/1,2,3,4</c></seealso>.</p>
+ <seemfa marker="#scan_erl_exprs/1">
+ <c>scan_erl_exprs/1,2,3,4</c></seemfa>.</p>
</desc>
</func>
@@ -1092,7 +1092,7 @@ enter><input>1.0er.</input>
<p>Possible options and values vary depending on the
I/O device. For a list of supported options and their current values
on a specific I/O device, use function
- <seealso marker="#getopts/1"><c>getopts/1</c></seealso>.</p>
+ <seemfa marker="#getopts/1"><c>getopts/1</c></seemfa>.</p>
<p>The options and values supported by the OTP I/O devices
are as follows:</p>
<taglist>
@@ -1102,10 +1102,10 @@ enter><input>1.0er.</input>
the I/O server sends binary data (encoded in UTF-8) as answers
to the <c>get_line</c>, <c>get_chars</c>, and, if possible,
<c>get_until</c> requests (for details, see section
- <seealso marker="io_protocol">The Erlang I/O Protocol</seealso>)
+ <seeguide marker="io_protocol">The Erlang I/O Protocol</seeguide>)
in the User's Guide). The immediate effect is that
- <seealso marker="#get_chars/2"><c>get_chars/2,3</c></seealso> and
- <seealso marker="#get_line/1"><c>get_line/1,2</c></seealso>
+ <seemfa marker="#get_chars/2"><c>get_chars/2,3</c></seemfa> and
+ <seemfa marker="#get_line/1"><c>get_line/1,2</c></seemfa>
return UTF-8 binaries instead of lists of characters
for the affected I/O device.</p>
<p>By default, all I/O devices in OTP are set in <c>list</c> mode.
@@ -1127,7 +1127,7 @@ enter><input>1.0er.</input>
like the Erlang shell. This function is called
when the user presses the <em>Tab</em> key. The expansion is
active when calling line-reading functions, such as
- <seealso marker="#get_line/1"><c>get_line/1,2</c></seealso>.</p>
+ <seemfa marker="#get_line/1"><c>get_line/1,2</c></seemfa>.</p>
<p>The function is called with the current line, up to
the cursor, as a reversed string. It is to return a
three-tuple: <c>{yes|no, string(), [string(), ...]}</c>. The
@@ -1190,8 +1190,8 @@ fun("") -> {yes, "quit", []};
<c>{encoding, unicode}</c> on files.</p>
<p>The extended encodings are only supported on disk files
(opened by function
- <seealso marker="kernel:file#open/2">
- <c>file:open/2</c></seealso>).</p>
+ <seemfa marker="kernel:file#open/2">
+ <c>file:open/2</c></seemfa>).</p>
</item>
</taglist>
</desc>
diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml
index 0e743867ba..3b7aea529e 100644
--- a/lib/stdlib/doc/src/io_lib.xml
+++ b/lib/stdlib/doc/src/io_lib.xml
@@ -33,11 +33,11 @@
<description>
<p>This module contains functions for converting to and from
strings (lists of characters). They are used for implementing the
- functions in the <seealso marker="io"><c>io</c></seealso> module.
+ functions in the <seeerl marker="io"><c>io</c></seeerl> module.
There is no guarantee that the
character lists returned from some of the functions are flat,
they can be deep lists. Function
- <seealso marker="lists#flatten/1"><c>lists:flatten/1</c></seealso>
+ <seemfa marker="lists#flatten/1"><c>lists:flatten/1</c></seemfa>
can be used for flattening deep lists.</p>
</description>
@@ -48,7 +48,7 @@
<datatype>
<name name="continuation"/>
<desc><p>A continuation as returned by
- <seealso marker="#fread/3"><c>fread/3</c></seealso>.</p>
+ <seemfa marker="#fread/3"><c>fread/3</c></seemfa>.</p>
</desc>
</datatype>
<datatype>
@@ -103,7 +103,7 @@
<fsummary>Build the output text for a preparsed format list.</fsummary>
<desc>
<p>For details, see
- <seealso marker="#scan_format/2"><c>scan_format/2</c></seealso>.</p>
+ <seemfa marker="#scan_format/2"><c>scan_format/2</c></seemfa>.</p>
</desc>
</func>
@@ -143,7 +143,7 @@
<p>Returns a character list that represents <c><anno>Data</anno></c>
formatted in accordance with <c><anno>Format</anno></c>.
For a detailed description of the available formatting options, see
- <seealso marker="io#fwrite/1"><c>io:fwrite/1,2,3</c></seealso>.
+ <seemfa marker="io#fwrite/1"><c>io:fwrite/1,2,3</c></seemfa>.
If the format string or argument list contains an error, a fault is
generated.</p>
<p>If and only if the Unicode translation modifier is used in the
@@ -163,8 +163,8 @@
<p>Returns a character list that represents <c><anno>Data</anno></c>
formatted in accordance with <c><anno>Format</anno></c> in
the same way as
- <seealso marker="#fwrite/2"><c>fwrite/2</c></seealso> and
- <seealso marker="#format/2"><c>format/2</c></seealso>,
+ <seemfa marker="#fwrite/2"><c>fwrite/2</c></seemfa> and
+ <seemfa marker="#format/2"><c>format/2</c></seemfa>,
but takes an extra argument, a list of options.</p>
<p>Valid option:</p>
<taglist>
@@ -187,7 +187,7 @@
<p>Tries to read <c><anno>String</anno></c> in accordance with the
control sequences in <c><anno>Format</anno></c>.
For a detailed description of the available formatting options, see
- <seealso marker="io#fread/3"><c>io:fread/3</c></seealso>. It is
+ <seemfa marker="io#fread/3"><c>io:fread/3</c></seemfa>. It is
assumed that <c><anno>String</anno></c> contains whole lines.</p>
<p>The function returns:</p>
<taglist>
@@ -331,9 +331,9 @@
printable characters, otherwise <c>false</c>.</p>
<p>What is a printable character in this case is determined by
startup flag <c>+pc</c> to the Erlang VM; see
- <seealso marker="io#printable_range/0">
- <c>io:printable_range/0</c></seealso> and
- <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p>
+ <seemfa marker="io#printable_range/0">
+ <c>io:printable_range/0</c></seemfa> and
+ <seecom marker="erts:erl"><c>erl(1)</c></seecom>.</p>
</desc>
</func>
@@ -355,12 +355,12 @@
corresponding tuples. This list can be passed to:</p>
<list type="bulleted">
<item>
- <p><seealso marker="#build_text/1"><c>build_text/1</c></seealso>
+ <p><seemfa marker="#build_text/1"><c>build_text/1</c></seemfa>
to have the same effect as <c>format(Format, Args)</c></p>
</item>
<item>
- <p><seealso marker="#unscan_format/1">
- <c>unscan_format/1</c></seealso> to get the corresponding pair
+ <p><seemfa marker="#unscan_format/1">
+ <c>unscan_format/1</c></seemfa> to get the corresponding pair
of <c>Format</c> and <c>Args</c> (with every <c>*</c> and
corresponding argument expanded to numeric values)</p>
</item>
@@ -378,7 +378,7 @@
and a list of arguments.</fsummary>
<desc>
<p>For details, see
- <seealso marker="#scan_format/2"><c>scan_format/2</c></seealso>.</p>
+ <seemfa marker="#scan_format/2"><c>scan_format/2</c></seemfa>.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml
index 84b5f62c7f..c2292d7d49 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>2016</year>
+ <year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -86,7 +86,7 @@
<item>
<p><c>ReplyAs</c> can be any datum and is returned in the
corresponding <c>io_reply</c>. The
- <seealso marker="stdlib:io"><c>io</c></seealso> module monitors the
+ <seeerl marker="stdlib:io"><c>io</c></seeerl> module monitors the
the I/O server and uses the monitor reference as the <c>ReplyAs</c>
datum. A more complicated client can have many outstanding I/O
requests to the same I/O server and can use different references (or
@@ -142,7 +142,7 @@
<item>
<p><c>Module</c>, <c>Function</c>, and <c>Args</c> denote a function
that is called to produce the data (like
- <seealso marker="stdlib:io_lib#format/2"><c>io_lib:format/2</c></seealso>).
+ <seemfa marker="stdlib:io_lib#format/2"><c>io_lib:format/2</c></seemfa>).
</p>
<p><c>Args</c> is a list of arguments to the function. The function is
to produce data in the specified <c>Encoding</c>. The I/O server is
@@ -164,20 +164,10 @@ ok
<list type="bulleted">
<item><c>Error</c> describes the error to the client, which can do
whatever it wants with it. The
- <seealso marker="stdlib:io"><c>io</c></seealso> module typically
+ <seeerl marker="stdlib:io"><c>io</c></seeerl> module typically
returns it "as is".</item>
</list>
- <p>For backward compatibility, the following <c>Request</c>s are also to be
- handled by an I/O server (they are not to be present after
- Erlang/OTP R15B):</p>
-
- <pre>
-{put_chars, Characters}
-{put_chars, Module, Function, Args}</pre>
-
- <p>These are to behave as <c>{put_chars, latin1, Characters}</c> and
- <c>{put_chars, latin1, Module, Function, Args}</c>, respectively.</p>
</section>
<section>
@@ -327,24 +317,11 @@ eof
<item>
<p><c>Error</c> describes the error to the client, which can do
whatever it wants with it. The
- <seealso marker="stdlib:io"><c>io</c></seealso> module typically
+ <seeerl marker="stdlib:io"><c>io</c></seeerl> module typically
returns it as is.</p>
</item>
</list>
- <p>For backward compatibility, the following <c>Request</c>s are also to be
- handled by an I/O server (they are not to be present after
- Erlang/OTP R15B):</p>
-
- <pre>
-{get_until, Prompt, Module, Function, ExtraArgs}
-{get_chars, Prompt, N}
-{get_line, Prompt}</pre>
-
- <p>These are to behave as
- <c>{get_until, latin1, Prompt, Module, Function, ExtraArgs}</c>,
- <c>{get_chars, latin1, Prompt, N}</c>, and
- <c>{get_line, latin1, Prompt}</c>, respectively.</p>
</section>
<section>
@@ -363,14 +340,14 @@ eof
<p>Notice that the <c>get_until</c> request allows for a function with the
data specified as always being a list. Also, the return value data from
such a function can be of any type (as is indeed the case when an
- <seealso marker="stdlib:io#fread/2"><c>io:fread/2,3</c></seealso>
+ <seemfa marker="stdlib:io#fread/2"><c>io:fread/2,3</c></seemfa>
request is sent to an I/O server).
The client must be prepared for data received as
answers to those requests to be in various forms. However, the I/O
server is to convert the results to binaries whenever possible (that is,
when the function supplied to <c>get_until</c> returns a list). This is
done in the example in section
- <seealso marker="#example_io_server">An Annotated and Working Example I/O Server</seealso>.
+ <seeguide marker="#example_io_server">An Annotated and Working Example I/O Server</seeguide>.
</p>
<p>An I/O server in binary mode affects the data sent to the client, so that
@@ -382,7 +359,7 @@ eof
<list type="bulleted">
<item><c>Opts</c> is a list of options in the format recognized by the
- <seealso marker="stdlib:proplists"><c>proplists</c></seealso> module
+ <seeerl marker="stdlib:proplists"><c>proplists</c></seeerl> module
(and by the I/O server).</item>
</list>
@@ -554,7 +531,7 @@ loop(State) -&gt;
end.</code>
<p>The main loop receives messages from the client (which can use the
- the <seealso marker="stdlib:io"><c>io</c></seealso> module to send
+ the <seeerl marker="stdlib:io"><c>io</c></seeerl> module to send
requests). For each request, the function <c>request/2</c> is called and a
reply is eventually sent using function <c>reply/3</c>.</p>
@@ -592,7 +569,7 @@ request({put_chars, Encoding, Module, Function, Args}, State) -&gt;
<p>The <c>Encoding</c> says how the characters in the request are
represented. We want to store the characters as lists in the ETS
table, so we convert them to lists using function
- <seealso marker="stdlib:unicode#characters_to_list/2"><c>unicode:characters_to_list/2</c></seealso>.
+ <seemfa marker="stdlib:unicode#characters_to_list/2"><c>unicode:characters_to_list/2</c></seemfa>.
The conversion function conveniently accepts the encoding types
<c>unicode</c> and <c>latin1</c>, so we can use <c>Encoding</c> directly.</p>
@@ -637,24 +614,6 @@ request({requests, Reqs}, State) -&gt;
function applying the requests in the list one after another, returning
the last result.</p>
- <p>We need to handle backward compatibility and the
- <seealso marker="kernel:file"><c>file</c></seealso> module (which
- uses the old requests until backward compatibility with pre-R13 nodes is
- no longer needed). Notice that the I/O server does not work with a simple
- <c>file:write/2</c> if these are not added:</p>
-
- <code>
-request({put_chars,Chars}, State) -&gt;
- request({put_chars,latin1,Chars}, State);
-request({put_chars,M,F,As}, State) -&gt;
- request({put_chars,latin1,M,F,As}, State);
-request({get_chars,Prompt,N}, State) -&gt;
- request({get_chars,latin1,Prompt,N}, State);
-request({get_line,Prompt}, State) -&gt;
- request({get_line,latin1,Prompt}, State);
-request({get_until, Prompt,M,F,As}, State) -&gt;
- request({get_until,latin1,Prompt,M,F,As}, State);</code>
-
<p><c>{error, request}</c> must be returned if the request is not
recognized:</p>
@@ -910,8 +869,8 @@ apply_update(Table, {Row, Col, List}) -&gt;
<p>This concludes the example. It is fully runnable and you can read or
write to the I/O server by using, for example, the
- <seealso marker="stdlib:io"><c>io</c></seealso> module or even the
- <seealso marker="kernel:file"><c>file</c></seealso> module. It is
+ <seeerl marker="stdlib:io"><c>io</c></seeerl> module or even the
+ <seeerl marker="kernel:file"><c>file</c></seeerl> module. It is
as simple as that to implement a fully fledged I/O server in Erlang.</p>
</section>
</chapter>
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index 2755fb3dce..9261ade998 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -281,7 +281,7 @@ flatmap(Fun, List1) ->
<name name="foldr" arity="3" since=""/>
<fsummary>Fold a function over a list.</fsummary>
<desc>
- <p>Like <seealso marker="#foldl/3"><c>foldl/3</c></seealso>, but the
+ <p>Like <seemfa marker="#foldl/3"><c>foldl/3</c></seemfa>, but the
list is traversed from right to left.</p>
<p><em>Example:</em></p>
<pre>
@@ -418,7 +418,7 @@ flatmap(Fun, List1) ->
otherwise <c>false</c>.</p>
<note>
<p>This function is retained for backward compatibility. Function
- <seealso marker="#keyfind/3"><c>keyfind/3</c></seealso>
+ <seemfa marker="#keyfind/3"><c>keyfind/3</c></seemfa>
is usually more convenient.</p>
</note>
</desc>
@@ -492,8 +492,8 @@ flatmap(Fun, List1) ->
<fsummary>Map and fold in one pass.</fsummary>
<desc>
<p>Combines the operations of
- <seealso marker="#map/2"><c>map/2</c></seealso> and
- <seealso marker="#foldl/3"><c>foldl/3</c></seealso> into one pass.</p>
+ <seemfa marker="#map/2"><c>map/2</c></seemfa> and
+ <seemfa marker="#foldl/3"><c>foldl/3</c></seemfa> into one pass.</p>
<p><em>Example:</em></p>
<p>Summing the elements in a list and double them at the same time:</p>
<pre>
@@ -508,8 +508,8 @@ flatmap(Fun, List1) ->
<fsummary>Map and fold in one pass.</fsummary>
<desc>
<p>Combines the operations of
- <seealso marker="#map/2"><c>map/2</c></seealso> and
- <seealso marker="#foldr/3"><c>foldr/3</c></seealso> into one pass.</p>
+ <seemfa marker="#map/2"><c>map/2</c></seemfa> and
+ <seemfa marker="#foldr/3"><c>foldr/3</c></seemfa> into one pass.</p>
</desc>
</func>
@@ -564,8 +564,8 @@ flatmap(Fun, List1) ->
<desc>
<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><anno>List2</anno></c> must be sorted according to the <seeerl
+ marker="#ordering_function">ordering function</seeerl>
<c><anno>Fun</anno></c> before evaluating this function.
<c><anno>Fun</anno>(<anno>A</anno>, <anno>B</anno>)</c> is to return
<c>true</c> if <c><anno>A</anno></c> compares less
@@ -653,7 +653,7 @@ c</pre>
> <input>lists:partition(fun(A) -> is_atom(A) end, [a,b,1,c,d,2,3,4,e]).</input>
{[a,b,c,d,e],[1,2,3,4]}</pre>
<p>For a different way to partition a list, see
- <seealso marker="#splitwith/2"><c>splitwith/2</c></seealso>.</p>
+ <seemfa marker="#splitwith/2"><c>splitwith/2</c></seemfa>.</p>
</desc>
</func>
@@ -761,8 +761,8 @@ length(lists:seq(From, To, Incr)) =:= (To - From + Incr) div Incr</code>
<fsummary>Sort a list.</fsummary>
<desc>
<p>Returns a list containing the sorted elements of
- <c><anno>List1</anno></c>, according to the <seealso
- marker="#ordering_function">ordering function</seealso>
+ <c><anno>List1</anno></c>, according to the <seeerl
+ marker="#ordering_function">ordering function</seeerl>
<c><anno>Fun</anno></c>. <c><anno>Fun</anno>(<anno>A</anno>,
<anno>B</anno>)</c> is to return <c>true</c> if <c><anno>A</anno></c>
compares less than or equal to <c><anno>B</anno></c> in the
@@ -799,7 +799,7 @@ splitwith(Pred, List) ->
> <input>lists:splitwith(fun(A) -> is_atom(A) end, [a,b,1,c,d,2,3,4,e]).</input>
{[a,b],[1,c,d,2,3,4,e]}</pre>
<p>For a different way to partition a list, see
- <seealso marker="#partition/2"><c>partition/2</c></seealso>.</p>
+ <seemfa marker="#partition/2"><c>partition/2</c></seemfa>.</p>
</desc>
</func>
@@ -946,8 +946,8 @@ splitwith(Pred, List) ->
<desc>
<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><anno>List2</anno></c> must be sorted according to the <seeerl
+ marker="#ordering_function">ordering function</seeerl>
<c>Fun</c> and contain no duplicates before evaluating this function.
<c><anno>Fun</anno>(<anno>A</anno>, <anno>B</anno>)</c> is to return
<c>true</c> if <c><anno>A</anno></c> compares less than or equal to
@@ -1009,8 +1009,8 @@ splitwith(Pred, List) ->
<desc>
<p>Returns a list containing the sorted elements of
<c><anno>List1</anno></c> where all except the first element of the
- elements comparing equal according to the <seealso
- marker="#ordering_function">ordering function</seealso>
+ elements comparing equal according to the <seeerl
+ marker="#ordering_function">ordering function</seeerl>
<c><anno>Fun</anno></c> have been deleted.
<c><anno>Fun</anno>(A, B)</c> is to return
<c>true</c> if <c>A</c> compares less than or equal to
diff --git a/lib/stdlib/doc/src/log_mf_h.xml b/lib/stdlib/doc/src/log_mf_h.xml
index b7c19bbdec..61b871227c 100644
--- a/lib/stdlib/doc/src/log_mf_h.xml
+++ b/lib/stdlib/doc/src/log_mf_h.xml
@@ -41,7 +41,7 @@
in any <c>gen_event</c> process. It logs onto disk all events that are
sent to an event manager. Each event is written as a binary, which makes
the logging very fast. However, a tool such as the Report Browser
- (<seealso marker="sasl:rb"><c>rb(3)</c></seealso>) must be used to read
+ (<seeerl marker="sasl:rb"><c>rb(3)</c></seeerl>) must be used to read
the files. The events are written to multiple files. When all files have
been used, the first one is reused and overwritten. The directory
location, the number of files, and the size of each file are configurable.
@@ -52,8 +52,8 @@
<datatypes>
<datatype>
<name name="args"/>
- <desc><p>Term to be sent to <seealso marker="gen_event#add_handler/3">
- <c>gen_event:add_handler/3</c></seealso>.</p>
+ <desc><p>Term to be sent to <seemfa marker="gen_event#add_handler/3">
+ <c>gen_event:add_handler/3</c></seemfa>.</p>
</desc>
</datatype>
</datatypes>
@@ -80,8 +80,8 @@
<section>
<title>See Also</title>
- <p><seealso marker="gen_event"><c>gen_event(3)</c></seealso>,
- <seealso marker="sasl:rb"><c>rb(3)</c></seealso></p>
+ <p><seeerl marker="gen_event"><c>gen_event(3)</c></seeerl>,
+ <seeerl marker="sasl:rb"><c>rb(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml
index 8e88882b56..c445149f3b 100644
--- a/lib/stdlib/doc/src/maps.xml
+++ b/lib/stdlib/doc/src/maps.xml
@@ -39,11 +39,11 @@
<desc>
<p>An iterator representing the associations in a map with keys of type
<c><anno>Key</anno></c> and values of type <c><anno>Value</anno></c>.</p>
- <p>Created using <seealso marker="#iterator-1"><c>maps:iterator/1</c></seealso>.</p>
- <p>Consumed by <seealso marker="#next-1"><c>maps:next/1</c></seealso>,
- <seealso marker="#filter-2"><c>maps:filter/2</c></seealso>,
- <seealso marker="#fold-3"><c>maps:fold/3</c></seealso> and
- <seealso marker="#map-2"><c>maps:map/2</c></seealso>.</p>
+ <p>Created using <seemfa marker="#iterator/1"><c>maps:iterator/1</c></seemfa>.</p>
+ <p>Consumed by <seemfa marker="#next/1"><c>maps:next/1</c></seemfa>,
+ <seemfa marker="#filter/2"><c>maps:filter/2</c></seemfa>,
+ <seemfa marker="#fold/3"><c>maps:fold/3</c></seemfa> and
+ <seemfa marker="#map/2"><c>maps:map/2</c></seemfa>.</p>
</desc>
</datatype>
@@ -197,7 +197,7 @@ false</code>
<fsummary>Create a map iterator.</fsummary>
<desc>
<p>Returns a map iterator <c><anno>Iterator</anno></c> that can
- be used by <seealso marker="#next-1"><c>maps:next/1</c></seealso>
+ be used by <seemfa marker="#next/1"><c>maps:next/1</c></seemfa>
to traverse the key-value associations in a map. When iterating
over a map, the memory usage is guaranteed to be bounded no matter
the size of the map.</p>
diff --git a/lib/stdlib/doc/src/math.xml b/lib/stdlib/doc/src/math.xml
index b2f4a4eb12..59a05a4e5a 100644
--- a/lib/stdlib/doc/src/math.xml
+++ b/lib/stdlib/doc/src/math.xml
@@ -42,8 +42,8 @@
<note>
<p>Not all functions are provided on all platforms. In particular,
- the <seealso marker="#erf/1"><c>erf/1</c></seealso> and
- <seealso marker="#erfc/1"><c>erfc/1</c></seealso> functions
+ the <seemfa marker="#erf/1"><c>erf/1</c></seemfa> and
+ <seemfa marker="#erfc/1"><c>erfc/1</c></seemfa> functions
are not provided on Windows.</p>
</note>
</description>
diff --git a/lib/stdlib/doc/src/ms_transform.xml b/lib/stdlib/doc/src/ms_transform.xml
index 7e156e2110..bde9223eb0 100644
--- a/lib/stdlib/doc/src/ms_transform.xml
+++ b/lib/stdlib/doc/src/ms_transform.xml
@@ -38,19 +38,19 @@
<description>
<marker id="top"></marker>
<p>This module provides the parse transformation that makes calls to
- <seealso marker="ets"><c>ets</c></seealso> and
- <seealso marker="runtime_tools:dbg#fun2ms/1"><c>dbg:fun2ms/1</c></seealso>
+ <seeerl marker="ets"><c>ets</c></seeerl> and
+ <seemfa marker="runtime_tools:dbg#fun2ms/1"><c>dbg:fun2ms/1</c></seemfa>
translate into literal match specifications. It also provides the back end
for the same functions when called from the Erlang shell.</p>
<p>The translation from funs to match specifications
is accessed through the two "pseudo functions"
- <seealso marker="ets#fun2ms/1"><c>ets:fun2ms/1</c></seealso> and
- <seealso marker="runtime_tools:dbg#fun2ms/1"><c>dbg:fun2ms/1</c></seealso>.</p>
+ <seemfa marker="ets#fun2ms/1"><c>ets:fun2ms/1</c></seemfa> and
+ <seemfa marker="runtime_tools:dbg#fun2ms/1"><c>dbg:fun2ms/1</c></seemfa>.</p>
<p>As everyone trying to use
- <seealso marker="ets#select/1"><c>ets:select/2</c></seealso> or
- <seealso marker="runtime_tools:dbg"><c>dbg</c></seealso> seems to end up
+ <seemfa marker="ets#select/1"><c>ets:select/2</c></seemfa> or
+ <seeerl marker="runtime_tools:dbg"><c>dbg</c></seeerl> seems to end up
reading this manual page, this description is an introduction to the
concept of match specifications.</p>
@@ -59,7 +59,7 @@
<p>Match specifications are used more or less as filters. They resemble
usual Erlang matching in a list comprehension or in a fun used with
- <seealso marker="lists#foldl/3"><c>lists:foldl/3</c></seealso>, and so on.
+ <seemfa marker="lists#foldl/3"><c>lists:foldl/3</c></seemfa>, and so on.
However, the syntax of pure match specifications is awkward, as
they are made up purely by Erlang terms, and the language has no
syntax to make the match specifications more readable.</p>
@@ -77,11 +77,11 @@
<section>
<title>Example 1</title>
- <p>Using <seealso marker="ets#select/2"><c>ets:select/2</c></seealso>
+ <p>Using <seemfa marker="ets#select/2"><c>ets:select/2</c></seemfa>
and a match specification, one can filter out rows of
a table and construct a list of tuples containing relevant parts
of the data in these rows.
- One can use <seealso marker="ets#foldl/3"><c>ets:foldl/3</c></seealso>
+ One can use <seemfa marker="ets#foldl/3"><c>ets:foldl/3</c></seemfa>
instead, but the <c>ets:select/2</c> call is far more efficient.
Without the translation provided by <c>ms_transform</c>,
one must struggle with writing match specifications terms
@@ -127,8 +127,8 @@ ets:new(emp_tab, [{keypos,#emp.empno},named_table,ordered_set]).</code>
but it is still unreadable, and one has little control over the
returned result. It is always a list of lists.</p>
- <p><seealso marker="ets#foldl/3"><c>ets:foldl/3</c></seealso> or
- <seealso marker="ets#foldr/3"><c>ets:foldr/3</c></seealso> can be used to avoid the nested lists:</p>
+ <p><seemfa marker="ets#foldl/3"><c>ets:foldl/3</c></seemfa> or
+ <seemfa marker="ets#foldr/3"><c>ets:foldr/3</c></seemfa> can be used to avoid the nested lists:</p>
<code type="none">
ets:foldr(fun(#emp{empno = E, dept = sales},Acc) -> [E | Acc];
@@ -262,8 +262,8 @@ ets:select(emp_tab, ets:fun2ms(
mention that variable A is simply translated into '$_'.
Alternatively, pseudo function <c>object/0</c>
also returns the whole matched object, see section
- <seealso marker="#warnings_and_restrictions">
- Warnings and Restrictions</seealso>.</p>
+ <seeerl marker="#warnings_and_restrictions">
+ Warnings and Restrictions</seeerl>.</p>
</section>
<section>
@@ -339,12 +339,12 @@ ets:select(emp_tab, ets:fun2ms(
<section>
<title>Useful BIFs</title>
<p>What more can you do? A simple answer is: see the documentation of
- <seealso marker="erts:match_spec">match specifications</seealso>
+ <seeguide marker="erts:match_spec">match specifications</seeguide>
in ERTS User's Guide.
However, the following is a brief overview of the most useful "built-in
functions" that you can use when the fun is to be translated into a match
specification by
- <seealso marker="ets#fun2ms/1"> <c>ets:fun2ms/1</c></seealso>. It is not
+ <seemfa marker="ets#fun2ms/1"> <c>ets:fun2ms/1</c></seemfa>. It is not
possible to call other functions than those allowed in match
specifications. No "usual" Erlang code can be executed by the fun that
is translated by <c>ets:fun2ms/1</c>. The fun is limited
@@ -423,8 +423,8 @@ ets:select(emp_tab, ets:fun2ms(
<section>
<title>Example with dbg</title>
<p>This section describes the slightly different match specifications
- translated by <seealso marker="runtime_tools:dbg#fun2ms/1">
- <c>dbg:fun2ms/1</c></seealso>.</p>
+ translated by <seemfa marker="runtime_tools:dbg#fun2ms/1">
+ <c>dbg:fun2ms/1</c></seemfa>.</p>
<p>The same reasons for using the parse transformation apply to
<c>dbg</c>, maybe even more, as filtering using Erlang code is
@@ -574,7 +574,7 @@ start(Args) ->
<p>This example illustrates the most used calls in match specifications for
<c>dbg</c>. The other, more esoteric, calls are listed and explained in
- <seealso marker="erts:match_spec">Match specifications in Erlang</seealso>
+ <seeguide marker="erts:match_spec">Match specifications in Erlang</seeguide>
in ERTS User's Guide, as they are beyond
the scope of this description.</p>
</section>
@@ -725,7 +725,7 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]).</code>
time, so runtime performance is not affected by using these pseudo
functions.</p>
<p>For more information about match specifications, see the
- <seealso marker="erts:match_spec">Match specifications in Erlang</seealso>
+ <seeguide marker="erts:match_spec">Match specifications in Erlang</seeguide>
in ERTS User's Guide.</p>
</section>
@@ -752,12 +752,12 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]).</code>
transformation if and when header file <c>ms_transform.hrl</c>
is included in the source code.</p>
<p>For information about how to use this parse transformation, see
- <seealso marker="ets"><c>ets</c></seealso> and
- <seealso marker="runtime_tools:dbg#fun2ms/1">
- <c>dbg:fun2ms/1</c></seealso>.</p>
+ <seeerl marker="ets"><c>ets</c></seeerl> and
+ <seemfa marker="runtime_tools:dbg#fun2ms/1">
+ <c>dbg:fun2ms/1</c></seemfa>.</p>
<p>For a description of match specifications, see section
- <seealso marker="erts:match_spec">
- Match Specification in Erlang</seealso> in ERTS User's Guide.</p>
+ <seeguide marker="erts:match_spec">
+ Match Specification in Erlang</seeguide> in ERTS User's Guide.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index f958bb796f..9c60fb860f 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,14 +31,406 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.14.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Handle maps in <c>erl_parse:tokens()</c>.</p>
+ <p>
+ Own Id: OTP-16978</p>
+ </item>
+ <item>
+ <p>
+ The erlang shell function <c>rr</c> has been fixed to be
+ able to read records from files within a code archive.</p>
+ <p>
+ Own Id: OTP-17182 Aux Id: PR-3002 </p>
+ </item>
+ <item>
+ <p>If <c>beam_lib</c> is asked to return abstract code
+ for a BEAM file produced by Elixir and Elixir is not
+ installed on the computer, <c>beam_lib</c> will no longer
+ crash, but will return an error tuple. The
+ <c>cover:compile_beam()</c> and
+ <c>cover:compile_beam_directory()</c> functions have been
+ updated to also return an error tuple in that
+ situation.</p>
+ <p>
+ Own Id: OTP-17194 Aux Id: GH-4353 </p>
+ </item>
+ <item>
+ <p>
+ Correct example module <c>erl_id_trans</c> regarding the
+ <c>{char, C}</c> type.</p>
+ <p>
+ Own Id: OTP-17273</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.14</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ This change fixes the handling of deep lists in the path
+ component when using uri_string:recompose/1.</p>
+ <p>
+ Own Id: OTP-16941</p>
+ </item>
+ <item>
+ <p>
+ Fix <seeerl
+ marker="shell_docs"><c>shell_docs</c></seeerl> to clear
+ shell decorations (bold/underline) when paginating
+ output.</p>
+ <p>
+ Fix various small renderings issues when integrating
+ <seeerl marker="shell_docs"><c>shell_docs</c></seeerl>
+ with edoc.</p>
+ <p>
+ Own Id: OTP-17047</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved the API and documentation of the uri_string
+ module.</p>
+ <p>
+ Added a new chapter to the Users Guide about Uniform
+ Resource Identifiers and their handling with the new API.</p>
+ <p>
+ Added two new API functions:
+ uri_string:allowed_characters/0 and
+ uri_string:percent_decode/1.</p>
+ <p>
+ This change has been marked as potentially incompatible
+ as uri_string:normalize/2 used to decode percent-encoded
+ character triplets that corresponded to characters not in
+ the reserved set. After this change,
+ uri_string:normalize/2 will only decode those
+ percent-encoded triplets that correspond to characters in
+ the unreserved set (ALPHA / DIGIT / "-" / "." / "_" /
+ "~").</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-16460</p>
+ </item>
+ <item>
+ <p>
+ The <c>shell_docs</c> module has been expanded with the
+ possibility to configure unicode, ansi and column size
+ for the rendered text.</p>
+ <p>
+ Own Id: OTP-16990</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.13.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The functions <c>digraph:in_edges/2</c> and
+ <c>digraph:out_edges/2</c> would return false edges if
+ called for a vertex that had a '_' atom in its name
+ term.</p>
+ <p>
+ Own Id: OTP-16655</p>
+ </item>
+ <item>
+ <p><c>filelib:wildcard("not-a-directory/..")</c> should
+ return an empty list. On Windows it returned
+ <c>"not-a-directory/.."</c>.</p>
+ <p>
+ Own Id: OTP-16700</p>
+ </item>
+ <item>
+ <p>
+ Fix the typespec of shell_docs:render to use the correct
+ type for an MFA.</p>
+ <p>
+ Own Id: OTP-16739</p>
+ </item>
+ <item>
+ <p>
+ Fix uri_string:recompose/1 when host is present but input
+ path is not absolute.</p>
+ <p>
+ This change prevents the recompose operation to change
+ the top level domain of the host when the path does not
+ start with a slash.</p>
+ <p>
+ Own Id: OTP-16751 Aux Id: ERL-1283 </p>
+ </item>
+ <item>
+ <p>The <c>epp</c> module would return a badly formed
+ error term when an '<c>if</c>' preprocessor directive
+ referenced an undefined symbol. <c>epp:format_error/1</c>
+ would crash when called with the bad error term.</p>
+ <p>
+ Own Id: OTP-16816 Aux Id: ERL-1310 </p>
+ </item>
+ <item>
+ <p>
+ <c>lists:sublist(List, Start, Len)</c> failed with an
+ exception if <c>Start &gt; length(List) + 1</c> even
+ though it is explicitly documented that "It is not an
+ error for <c>Start+Len</c> to exceed the length of the
+ list".</p>
+ <p>
+ Own Id: OTP-16830 Aux Id: ERL-1334, PR-2718 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.13.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>When a temporary child of a <c>simple_one_for_one
+ supervisor</c> died, the internal state of the supervisor
+ would be corrupted in a way that would cause the
+ supervisor to retain the start arguments for subsequent
+ children started by the supervisor, causing unnecessary
+ growth of the supervisor's heap. There state corruption
+ could potentially cause other problems as well.</p>
+ <p>
+ Own Id: OTP-16804</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 3.13</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Compiling a match specification with excessive nesting
+ caused the runtime system to crash due to scheduler stack
+ exhaustion. Instead of crashing the runtime system,
+ effected functions will now raise a <c>system_limit</c>
+ error exception in this situation.</p>
+ <p>
+ Own Id: OTP-16431 Aux Id: ERL-592 </p>
+ </item>
+ <item>
+ <p> Initialization of record fields using <c>_</c> is no
+ longer allowed if the number of affected fields is zero.
+ </p>
+ <p>
+ Own Id: OTP-16516</p>
+ </item>
+ <item>
+ <p> Fix bugs in <c>eval_bits</c>. </p>
+ <p>
+ Own Id: OTP-16545</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved the printout of single line logger events for
+ most of the OTP behaviours in STDLIB and Kernel. This
+ includes <c>proc_lib</c>, <c>gen_server</c>,
+ <c>gen_event</c>, <c>gen_statem</c>, <c>gen_fsm</c>,
+ <c>supervisor</c>, <c>supervisor_bridge</c> and
+ <c>application</c>.</p>
+ <p>
+ Improved the <seeerl
+ marker="kernel:logger_formatter#chars_limit"><c>chars_limit</c></seeerl>
+ and <seeerl
+ marker="kernel:logger_formatter#depth"><c>depth</c></seeerl>
+ handling in <c>proc_lib</c> and when formatting of
+ exceptions.</p>
+ <p>
+ Own Id: OTP-15299</p>
+ </item>
+ <item>
+ <p>
+ Remove usage and documentation of old requests of the
+ I/O-protocol.</p>
+ <p>
+ Own Id: OTP-15695</p>
+ </item>
+ <item>
+ <p>Improved ETS scalability of concurrent calls that
+ change the size of a table, like <c>ets:insert/2</c> and
+ <c>ets:delete/2</c>.</p> <p>This performance feature was
+ implemented for <c>ordered_set</c> in OTP 22.0 and does
+ now apply for all ETS table types.</p> <p>The improved
+ scalability may come at the cost of longer latency of
+ <c>ets:info(T,size)</c> and <c>ets:info(T,memory)</c>. A
+ new table option <c>decentralized_counters</c> has
+ therefore been added. It is default <c>true</c> for
+ <c>ordered_set</c> with <c>write_concurrency</c> enabled
+ and default <c>false</c> for all other table types.</p>
+ <p>
+ Own Id: OTP-15744 Aux Id: OTP-15623, PR-2229 </p>
+ </item>
+ <item>
+ <p> Handle Unicode filenames in the <c>zip</c> module.
+ </p>
+ <p>
+ Own Id: OTP-16005 Aux Id: ERL-1003, ERL-1150 </p>
+ </item>
+ <item>
+ <p>
+ Unicode support was updated to the Unicode 12.1 standard.</p>
+ <p>
+ Own Id: OTP-16073 Aux Id: PR-2339 </p>
+ </item>
+ <item>
+ <p>
+ All of the modules <seemfa
+ marker="stdlib:proc_lib#start_monitor/3"><c>proc_lib</c></seemfa>,
+ <seemfa
+ marker="stdlib:gen_server#start_monitor/3"><c>gen_server</c></seemfa>,
+ <seemfa
+ marker="stdlib:gen_statem#start_monitor/3"><c>gen_statem</c></seemfa>,
+ and <seemfa
+ marker="stdlib:gen_event#start_monitor/0"><c>gen_event</c></seemfa>
+ have been extended with a <c>start_monitor()</c>
+ function. For more information, see the documentation of
+ <c>start_monitor()</c> for these modules.</p>
+ <p>
+ Own Id: OTP-16120 Aux Id: ERIERL-402, PR-2427 </p>
+ </item>
+ <item>
+ <p>
+ Updates for new <c>erlang:term_to_iovec()</c> BIF.</p>
+ <p>
+ Own Id: OTP-16128 Aux Id: OTP-15618 </p>
+ </item>
+ <item>
+ <p>Documented a quirk regarding extraction from file
+ descriptors in <c>erl_tar</c>.</p>
+ <p>
+ Own Id: OTP-16171 Aux Id: ERL-1057 </p>
+ </item>
+ <item>
+ <p>
+ Added <c>ok</c> as return value to
+ <c>gen_server:reply/2</c></p>
+ <p>
+ Own Id: OTP-16210 Aux Id: PR-2411 </p>
+ </item>
+ <item>
+ <p>New functions have been added to <seeerl
+ marker="c"><c>c(3)</c></seeerl> for printing embedded
+ documentation for Erlang modules. The functions are:</p>
+ <taglist> <tag>h/1,2,3</tag> <item>Print the
+ documentation for a Module:Function/Arity.</item>
+ <tag>ht/1,2,3</tag> <item>Print the type documentation
+ for a Module:Type/Arity.</item> </taglist> <p>The
+ embedded documentation is created when building the
+ Erlang/OTP documentation.</p>
+ <p>
+ Own Id: OTP-16222</p>
+ </item>
+ <item>
+ <p> Add <c>indent</c> and <c>linewidth</c> to the options
+ of the <c>erl_pp</c> module's functions. </p>
+ <p>
+ Own Id: OTP-16276 Aux Id: PR-2443 </p>
+ </item>
+ <item>
+ <p>
+ Minor updates due to the new spawn improvements made.</p>
+ <p>
+ Own Id: OTP-16368 Aux Id: OTP-15251 </p>
+ </item>
+ <item>
+ <p>The compiler will now raise a warning when inlining is
+ used in modules that load NIFs.</p>
+ <p>
+ Own Id: OTP-16429 Aux Id: ERL-303 </p>
+ </item>
+ <item>
+ <p>Refactored the internal handling of deprecated and
+ removed functions.</p>
+ <p>
+ Own Id: OTP-16469</p>
+ </item>
+ <item>
+ <p> Extend <c>erl_parse:abstract/1,2</c> to handle
+ external fun expressions (<c>fun M:F/A</c>). </p>
+ <p>
+ Own Id: OTP-16480</p>
+ </item>
+ <item>
+ <p>Added <c>filelib:safe_relative_path/2</c> to replace
+ <c>filename:safe_relative_path/1</c>, which did not
+ safely handle symbolic links.</p>
+ <p><c>filename:safe_relative_path/1</c> has been
+ deprecated.</p>
+ <p>
+ Own Id: OTP-16483 Aux Id: PR-2542 </p>
+ </item>
+ <item>
+ <p>
+ The module <c>shell_docs</c> has been added. The module
+ contains functions for rendering, validating and
+ normalizing embedded documentation.</p>
+ <p>
+ Own Id: OTP-16500</p>
+ </item>
+ <item>
+ <p>
+ Module and function auto-completion in the shell now
+ looks at all available modules instead of only those
+ loaded. A module is considered available if it either is
+ loaded already or would be loaded if called.</p>
+ <p>
+ The auto-completion has also been expanded to work in the
+ new <c>h/1,2,3</c> function in <c>c(3)</c>.</p>
+ <p>
+ Own Id: OTP-16501 Aux Id: OTP-16494, OTP-16222,
+ OTP-16406, OTP-16499, OTP-16500, PR-2545, ERL-708 </p>
+ </item>
+ <item>
+ <p>Updated the internal <c>pcre</c> library to
+ <c>8.44</c>.</p>
+ <p>
+ Own Id: OTP-16557</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.12.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
<p>
- <seealso marker="stdlib:re#run/3">re:run(Subject, RE,
- [unicode])</seealso> returned <c>nomatch</c> instead of
+ <seemfa marker="stdlib:re#run/3">re:run(Subject, RE,
+ [unicode])</seemfa> returned <c>nomatch</c> instead of
failing with a <c>badarg</c> error exception when
<c>Subject</c> contained illegal utf8 and <c>RE</c> was
passed as a binary. This has been corrected along with
@@ -161,10 +553,10 @@
<list>
<item>
<p>
- The functions <seealso
- marker="stdlib:unicode#characters_to_list/2"><c>unicode:characters_to_list()</c></seealso>
- and <seealso
- marker="stdlib:unicode#characters_to_binary/3"><c>unicode:characters_to_binary()</c></seealso>
+ The functions <seemfa
+ marker="stdlib:unicode#characters_to_list/2"><c>unicode:characters_to_list()</c></seemfa>
+ and <seemfa
+ marker="stdlib:unicode#characters_to_binary/3"><c>unicode:characters_to_binary()</c></seemfa>
raised a <c>badarg</c> exception instead of returning an
error tuple when passed very large invalid code points as
input.</p>
@@ -186,8 +578,8 @@
</item>
<item>
<p>
- Fixed erroneous type spec for <seealso
- marker="stdlib:binary#list_to_bin/1"><c>binary:list_to_bin/1</c></seealso>.
+ Fixed erroneous type spec for <seemfa
+ marker="stdlib:binary#list_to_bin/1"><c>binary:list_to_bin/1</c></seemfa>.
Argument type was changed from <c>iodata()</c> to
<c>iolist()</c>.</p>
<p>
@@ -253,8 +645,8 @@
to version 8.43. See <url
href="http://pcre.org/original/changelog.txt">http://pcre.org/original/changelog.txt</url>
for information about changes made to PCRE. This library
- implements major parts of the <seealso
- marker="stdlib:re"><c>re</c></seealso> regular
+ implements major parts of the <seeerl
+ marker="stdlib:re"><c>re</c></seeerl> regular
expressions module.</p>
<p>
Own Id: OTP-15889</p>
@@ -653,6 +1045,27 @@
</section>
+<section><title>STDLIB 3.8.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <seemfa marker="stdlib:re#run/3">re:run(Subject, RE,
+ [unicode])</seemfa> returned <c>nomatch</c> instead of
+ failing with a <c>badarg</c> error exception when
+ <c>Subject</c> contained illegal utf8 and <c>RE</c> was
+ passed as a binary. This has been corrected along with
+ corrections of reduction counting in <c>re:run()</c>
+ error cases.</p>
+ <p>
+ Own Id: OTP-16553</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.8.2.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -965,18 +1378,18 @@
Own Id: OTP-14019 Aux Id: ERL-550 </p>
</item>
<item>
- <p> File operations used to accept <seealso
- marker="kernel:file#type-name_all">filenames</seealso>
+ <p> File operations used to accept <seetype
+ marker="kernel:file#name_all">filenames</seetype>
containing null characters (integer value zero). This
caused the name to be truncated and in some cases
arguments to primitive operations to be mixed up.
Filenames containing null characters inside the filename
are now <em>rejected</em> and will cause primitive file
operations to fail. </p> <p> Also environment variable
- operations used to accept <seealso
- marker="kernel:os#type-env_var_name">names</seealso> and
- <seealso
- marker="kernel:os#type-env_var_value">values</seealso> of
+ operations used to accept <seetype
+ marker="kernel:os#env_var_name">names</seetype> and
+ <seetype
+ marker="kernel:os#env_var_value">values</seetype> of
environment variables containing null characters (integer
value zero). This caused operations to silently produce
erroneous results. Environment variable names and values
@@ -987,12 +1400,12 @@
character in environment variable names causing various
problems. <c>$=</c> characters in environment variable
names are now also <em>rejected</em>. </p> <p>Also
- <seealso
- marker="kernel:os#cmd/1"><c>os:cmd/1</c></seealso> now
- reject null characters inside its <seealso
- marker="kernel:os#type-os_command">command</seealso>.
- </p> <p><seealso
- marker="erts:erlang#open_port/2"><c>erlang:open_port/2</c></seealso>
+ <seemfa
+ marker="kernel:os#cmd/1"><c>os:cmd/1</c></seemfa> now
+ reject null characters inside its <seetype
+ marker="kernel:os#os_command">command</seetype>.
+ </p> <p><seemfa
+ marker="erts:erlang#open_port/2"><c>erlang:open_port/2</c></seemfa>
will also reject null characters inside the port name
from now on.</p>
<p>
@@ -1073,10 +1486,10 @@
</item>
<item>
<p>A new logging API is added to Erlang/OTP, see the
- <seealso
- marker="kernel:logger"><c>logger(3)</c></seealso> manual
- page, and section <seealso
- marker="kernel:logger_chapter">Logging</seealso> in the
+ <seeerl
+ marker="kernel:logger"><c>logger(3)</c></seeerl> manual
+ page, and section <seeguide
+ marker="kernel:logger_chapter">Logging</seeguide> in the
Kernel User's Guide.</p>
<p>Calls to <c>error_logger</c> are automatically
redirected to the new API, and legacy error logger event
@@ -1662,9 +2075,9 @@
Own Id: OTP-13830</p>
</item>
<item>
- <p>Replaced usage of deprecated symbolic <seealso
- marker="erts:erlang#type-time_unit"><c>time
- unit</c></seealso> representations.</p>
+ <p>Replaced usage of deprecated symbolic <seetype
+ marker="erts:erlang#time_unit"><c>time
+ unit</c></seetype> representations.</p>
<p>
Own Id: OTP-13831 Aux Id: OTP-13735 </p>
</item>
@@ -1909,7 +2322,7 @@
<p>
Upgraded the OTP internal PCRE library from version 8.33
to version 8.40. This library is used for implementation
- of the <seealso marker="stdlib:re"><c>re</c></seealso>
+ of the <seeerl marker="stdlib:re"><c>re</c></seeerl>
regular expressions module.</p>
<p>
Besides various bug fixes, the new version allows for
@@ -1917,10 +2330,10 @@
feature, the stack size of normal scheduler threads is
now by default set to 128 kilo words on all platforms.
The stack size of normal scheduler threads can be set
- upon system start by passing the <seealso
- marker="erts:erl#sched_thread_stack_size"><c>+sss</c></seealso>
- command line argument to the <seealso
- marker="erts:erl"><c>erl</c></seealso> command.</p>
+ upon system start by passing the <seecom
+ marker="erts:erl#sched_thread_stack_size"><c>+sss</c></seecom>
+ command line argument to the <seecom
+ marker="erts:erl"><c>erl</c></seecom> command.</p>
<p>
See <url
href="http://pcre.org/original/changelog.txt">http://pcre.org/original/changelog.txt</url>
@@ -2892,10 +3305,10 @@
Earlier, supervisor flags and child specs were given as
tuples. While this is kept for backwards compatibility,
it is now also allowed to give these parameters as maps,
- see <seealso
- marker="stdlib:supervisor#sup_flags">sup_flags</seealso>
- and <seealso
- marker="stdlib:supervisor#child_spec">child_spec</seealso>.</p>
+ see <seeerl
+ marker="stdlib:supervisor#sup_flags">sup_flags</seeerl>
+ and <seeerl
+ marker="stdlib:supervisor#child_spec">child_spec</seeerl>.</p>
<p>
Own Id: OTP-11043</p>
</item>
@@ -3648,10 +4061,10 @@
and yield without having to set up the heap in a state
that can be garbage collected.</p>
<p>
- The <seealso
- marker="erts:erlang#garbage_collect/2"><c>garbage_collect/2</c></seealso>,
- and <seealso
- marker="erts:erlang#check_process_code/3"><c>check_process_code/3</c></seealso>
+ The <seemfa
+ marker="erts:erlang#garbage_collect/2"><c>garbage_collect/2</c></seemfa>,
+ and <seemfa
+ marker="erts:erlang#check_process_code/3"><c>check_process_code/3</c></seemfa>
BIFs have been introduced. Both taking an option list as
last argument. Using these, one can issue asynchronous
requests.</p>
@@ -3706,8 +4119,8 @@
</taglist>
<p>
For information on how to use Maps please see Map Expressions in the
- <seealso marker="doc/reference_manual:expressions#map_expressions">
- Reference Manual</seealso>.</p>
+ <seeguide marker="system/reference_manual:expressions#map_expressions">
+ Reference Manual</seeguide>.</p>
<p>
The current implementation is without the following
features:</p>
@@ -5508,15 +5921,15 @@
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
+ ETS tables by passing the <seeerl
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
+ true}</seeerl> option upon table creation. See the
+ documentation of <seemfa
+ marker="stdlib:ets#new/2">ets:new/2</seemfa> 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
+ more information, see the documentation of the <seecom
+ marker="erts:erl#+rg">+rg</seecom> command line argument
of <c>erl</c>.</p>
<p>
There is also a new implementation of rwlocks that is not
@@ -5558,8 +5971,8 @@
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#Advanced-configuration-and-build-of-ErlangOTP">--with-libatomic_ops=PATH</seealso>
+ <c>libatomic_ops</c> library. Use the <seeguide
+ marker="system/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP">--with-libatomic_ops=PATH</seeguide>
<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:
@@ -5576,8 +5989,8 @@
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#Advanced-configuration-and-build-of-ErlangOTP">--enable-ethread-pre-pentium4-compatibility</seealso>
+ need to pass the <seeguide
+ marker="system/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP">--enable-ethread-pre-pentium4-compatibility</seeguide>
<c>configure</c> command line argument when configuring
the system.</p>
<p>
@@ -6100,10 +6513,10 @@
regarding <c>gen_server</c>, <c>gen_fsm</c>, and
<c>gen_event</c> behavior when handling <c>'EXIT'</c>
messages from the parent process. For more information
- see the <seealso
- marker="gen_server">gen_server(3)</seealso>, <seealso
- marker="gen_fsm">gen_fsm(3)</seealso>, and <seealso
- marker="gen_event">gen_event(3)</seealso> documentation.</p>
+ see the <seeerl
+ marker="gen_server">gen_server(3)</seeerl>, <seeerl
+ marker="gen_fsm">gen_fsm(3)</seeerl>, and <seeerl
+ marker="gen_event">gen_event(3)</seeerl> documentation.</p>
<p>
Own Id: OTP-8255 Aux Id: seq11419 </p>
</item>
diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml
index d5ceca9e0c..796cb42ede 100644
--- a/lib/stdlib/doc/src/orddict.xml
+++ b/lib/stdlib/doc/src/orddict.xml
@@ -41,7 +41,7 @@
ordered after the keys in the <em>Erlang term order</em>.</p>
<p>This module provides the same interface as the
- <seealso marker="dict"><c>dict(3)</c></seealso> module
+ <seeerl marker="dict"><c>dict(3)</c></seeerl> module
but with a defined representation. One difference is
that while <c>dict</c> considers two keys as different if they
do not match (<c>=:=</c>), this module considers two keys as
@@ -52,7 +52,7 @@
<datatype>
<name name="orddict" n_vars="2"/>
<desc><p>Dictionary as returned by
- <seealso marker="#new/0"><c>new/0</c></seealso>.</p></desc>
+ <seemfa marker="#new/0"><c>new/0</c></seemfa>.</p></desc>
</datatype>
<datatype>
<name name="orddict" n_vars="0"/>
@@ -68,7 +68,7 @@
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>
- <p>See also section <seealso marker="#notes">Notes</seealso>.</p>
+ <p>See also section <seeerl marker="#notes">Notes</seeerl>.</p>
</desc>
</func>
@@ -80,7 +80,7 @@
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>
- <p>See also section <seealso marker="#notes">Notes</seealso>.</p>
+ <p>See also section <seeerl marker="#notes">Notes</seeerl>.</p>
</desc>
</func>
@@ -100,7 +100,7 @@
in dictionary <c><anno>Orddict</anno></c>. This function assumes that
the <c><anno>Key</anno></c> is present in the dictionary. An exception
is generated if <c><anno>Key</anno></c> is not in the dictionary.</p>
- <p>See also section <seealso marker="#notes">Notes</seealso>.</p>
+ <p>See also section <seeerl marker="#notes">Notes</seeerl>.</p>
</desc>
</func>
@@ -140,7 +140,7 @@
<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>
- <p>See also section <seealso marker="#notes">Notes</seealso>.</p>
+ <p>See also section <seeerl marker="#notes">Notes</seeerl>.</p>
</desc>
</func>
@@ -312,8 +312,8 @@ update_counter(Key, Incr, D) ->
<section>
<title>See Also</title>
- <p><seealso marker="dict"><c>dict(3)</c></seealso>,
- <seealso marker="gb_trees"><c>gb_trees(3)</c></seealso></p>
+ <p><seeerl marker="dict"><c>dict(3)</c></seeerl>,
+ <seeerl marker="gb_trees"><c>gb_trees(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/ordsets.xml b/lib/stdlib/doc/src/ordsets.xml
index fbe334c009..1b16f45a7d 100644
--- a/lib/stdlib/doc/src/ordsets.xml
+++ b/lib/stdlib/doc/src/ordsets.xml
@@ -43,7 +43,7 @@
according to the <em>Erlang term order</em>.</p>
<p>This module provides the same interface as the
- <seealso marker="sets"><c>sets(3)</c></seealso> module
+ <seeerl marker="sets"><c>sets(3)</c></seeerl> module
but with a defined representation. One difference is
that while <c>sets</c> considers two elements as different if they
do not match (<c>=:=</c>), this module considers two elements as
@@ -54,7 +54,7 @@
<datatype>
<name name="ordset" n_vars="1"/>
<desc><p>As returned by
- <seealso marker="#new/0"><c>new/0</c></seealso>.</p></desc>
+ <seemfa marker="#new/0"><c>new/0</c></seemfa>.</p></desc>
</datatype>
</datatypes>
@@ -222,8 +222,8 @@
<section>
<title>See Also</title>
- <p><seealso marker="gb_sets"><c>gb_sets(3)</c></seealso>,
- <seealso marker="sets"><c>sets(3)</c></seealso></p>
+ <p><seeerl marker="gb_sets"><c>gb_sets(3)</c></seeerl>,
+ <seeerl marker="sets"><c>sets(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/part.xml b/lib/stdlib/doc/src/part.xml
index 93c47405bf..b6a2f16b57 100644
--- a/lib/stdlib/doc/src/part.xml
+++ b/lib/stdlib/doc/src/part.xml
@@ -37,5 +37,6 @@
<xi:include href="introduction.xml"/>
<xi:include href="io_protocol.xml"/>
<xi:include href="unicode_usage.xml"/>
+ <xi:include href="uri_string_usage.xml"/>
</part>
diff --git a/lib/stdlib/doc/src/pool.xml b/lib/stdlib/doc/src/pool.xml
index e17aff1b5b..ac67df2a2f 100644
--- a/lib/stdlib/doc/src/pool.xml
+++ b/lib/stdlib/doc/src/pool.xml
@@ -47,7 +47,7 @@
processes in the Erlang runtime system.</p>
<p>The slave nodes are started with the
- <seealso marker="slave"><c>slave(3)</c></seealso> module. This
+ <seeerl marker="slave"><c>slave(3)</c></seeerl> module. This
effects terminal I/O, file I/O, and code loading.</p>
<p>If the master node fails, the entire pool exits.</p>
</description>
@@ -106,10 +106,10 @@
<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>. The
+ section <seeerl marker="#files">Files</seeerl>. The
startup procedure fails if the file is not found.</p>
<p>The slave nodes are started with
- <seealso marker="slave#start/2"><c>slave:start/2,3</c></seealso>,
+ <seemfa marker="slave#start/2"><c>slave:start/2,3</c></seemfa>,
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
@@ -135,8 +135,8 @@
<title>Files</title>
<p><c>.hosts.erlang</c> is used to pick hosts where nodes can
be started. For information about format and location of this file, see
- <seealso marker="kernel:net_adm#host_file/0">
- <c>net_adm:host_file/0</c></seealso>.</p>
+ <seemfa marker="kernel:net_adm#host_file/0">
+ <c>net_adm:host_file/0</c></seemfa>.</p>
<p><c>$HOME/.erlang.slave.out.HOST</c> is used for all extra I/O
that can come from the slave nodes on standard I/O. If the startup
procedure does not work, this file can indicate the reason.</p>
diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml
index 2c83005a56..aa649a280a 100644
--- a/lib/stdlib/doc/src/proc_lib.xml
+++ b/lib/stdlib/doc/src/proc_lib.xml
@@ -33,15 +33,15 @@
adhering to the OTP design principles.</modulesummary>
<description>
<p>This module is used to start processes adhering to
- the <seealso marker="doc/design_principles:des_princ">
- OTP Design Principles</seealso>. Specifically, the functions in this
+ the <seeguide marker="system/design_principles:des_princ">
+ OTP Design Principles</seeguide>. Specifically, the functions in this
module are used by the OTP standard behaviors (for example,
<c>gen_server</c> and <c>gen_statem</c>)
when starting new processes. The functions
can also be used to start <em>special processes</em>, user-defined
processes that comply to the OTP design principles. For an example,
- see section <seealso marker="doc/design_principles:spec_proc">
- sys and proc_lib</seealso> in OTP Design Principles.</p>
+ see section <seeguide marker="system/design_principles:spec_proc">
+ sys and proc_lib</seeguide> in OTP Design Principles.</p>
<p>Some useful information is initialized when a process starts.
@@ -62,7 +62,7 @@
is generated, which is written to terminal by the default logger
handler setup by Kernel. For more information about how crash reports
were logged prior to Erlang/OTP 21.0, see
- <seealso marker="sasl:error_logging">SASL Error Logging</seealso>
+ <seeguide marker="sasl:error_logging">SASL Error Logging</seeguide>
in the SASL User's Guide.</p>
<p>Unlike in "plain Erlang", <c>proc_lib</c> processes will not generate
@@ -81,18 +81,16 @@
<datatype>
<name name="spawn_option"/>
<desc>
- <p>See <seealso marker="erts:erlang#spawn_opt/4">
- <c>erlang:spawn_opt/2,3,4,5</c></seealso>.</p>
+ <p>See <seemfa marker="erts:erlang#spawn_opt/4">
+ <c>erlang:spawn_opt/2,3,4,5</c></seemfa>.</p>
</desc>
</datatype>
<datatype>
- <name name="priority_level"/>
- </datatype>
- <datatype>
- <name name="max_heap_size"/>
+ <name name="start_spawn_option"/>
<desc>
- <p>See <seealso marker="erts:erlang#process_flag_max_heap_size">
- erlang:process_flag(max_heap_size, MaxHeapSize)</seealso>.</p>
+ <p>A restricted set of <seetype marker="#spawn_option">spawn
+ options</seetype>. Most notably <c>monitor</c> is <em>not</em> part
+ of these options.</p>
</desc>
</datatype>
<datatype>
@@ -105,8 +103,8 @@
<name name="format" arity="1" since=""/>
<fsummary>Format a crash report.</fsummary>
<desc>
- <p>Equivalent to <seealso marker="#format/2">
- <c>format(<anno>CrashReport</anno>, latin1)</c></seealso>.</p>
+ <p>Equivalent to <seemfa marker="#format/2">
+ <c>format(<anno>CrashReport</anno>, latin1)</c></seemfa>.</p>
</desc>
</func>
@@ -118,8 +116,8 @@
<p>This function is deprecated in the sense that
the <c>error_logger</c> is no longer the preferred
interface for logging in Erlang/OTP. A
- new <seealso marker="kernel:logger_chapter">logging
- API</seealso> was added in Erlang/OTP 21.0, but
+ new <seeguide marker="kernel:logger_chapter">logging
+ API</seeguide> was added in Erlang/OTP 21.0, but
legacy <c>error_logger</c> handlers can still be used. New
Logger handlers do not need to use this function, since
the formatting callback (<c>report_cb</c>) is included as
@@ -128,8 +126,8 @@
<p>This function can be used by a user-defined legacy
<c>error_logger</c> event handler to
format a crash report. The crash report is sent using
- <seealso marker="kernel:logger">
- <c>logger(3)</c></seealso>, and the event to be handled is of the format
+ <seeerl marker="kernel:logger">
+ <c>logger(3)</c></seeerl>, and the event to be handled is of the format
<c>{error_report, GL, {Pid, crash_report,
<anno>CrashReport</anno>}}</c>,
where <c>GL</c> is the group leader pid of process
@@ -145,8 +143,8 @@
<p>This function is deprecated in the sense that
the <c>error_logger</c> is no longer the preferred
interface for logging in Erlang/OTP. A
- new <seealso marker="kernel:logger_chapter">logging
- API</seealso> was added in Erlang/OTP 21.0, but
+ new <seeguide marker="kernel:logger_chapter">logging
+ API</seeguide> was added in Erlang/OTP 21.0, but
legacy <c>error_logger</c> handlers can still be used. New
Logger handlers do not need to used this function, since
the formatting callback (<c>report_cb</c>) is included as
@@ -166,8 +164,8 @@
<fsummary>Hibernate a process until a message is sent to it.</fsummary>
<desc>
<p>This function does the same as (and does call) the
- <seealso marker="erts:erlang#hibernate/3">
- <c>hibernate/3</c></seealso> BIF,
+ <seemfa marker="erts:erlang#hibernate/3">
+ <c>hibernate/3</c></seemfa> BIF,
but ensures that exception handling and logging continues to
work as expected when the process wakes up.</p>
<p>Always use this function instead of the BIF for processes started
@@ -181,7 +179,7 @@
<fsummary>Used by a process when it has started.</fsummary>
<desc>
<p>This function must be used by a process that has been started by
- a <seealso marker="#start/3"><c>start[_link]/3,4,5</c></seealso>
+ a <seemfa marker="#start/3"><c>start[_link]/3,4,5</c></seemfa>
function. It tells <c><anno>Parent</anno></c> that the process has
initialized itself, has started, or has failed to initialize
itself.</p>
@@ -257,7 +255,7 @@ init(Parent) ->
<desc>
<p>Spawns a new process and initializes it as described in the
beginning of this manual page. The process is spawned using the
- <seealso marker="erts:erlang#spawn/1"><c>spawn</c></seealso> BIFs.</p>
+ <seemfa marker="erts:erlang#spawn/1"><c>spawn</c></seemfa> BIFs.</p>
</desc>
</func>
@@ -275,7 +273,7 @@ init(Parent) ->
<desc>
<p>Spawns a new process and initializes it as described in the
beginning of this manual page. The process is spawned using the
- <seealso marker="erts:erlang#spawn_link/1"><c>spawn_link</c></seealso>
+ <seemfa marker="erts:erlang#spawn_link/1"><c>spawn_link</c></seemfa>
BIFs.</p>
</desc>
</func>
@@ -295,8 +293,31 @@ init(Parent) ->
<desc>
<p>Spawns a new process and initializes it as described in the
beginning of this manual page. The process is spawned using the
- <seealso marker="erts:erlang#spawn_opt/2"><c>spawn_opt</c></seealso>
+ <seemfa marker="erts:erlang#spawn_opt/2"><c>erlang:spawn_opt</c></seemfa>
BIFs.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="start" arity="3" since=""/>
+ <name name="start" arity="4" since=""/>
+ <name name="start" arity="5" since=""/>
+ <fsummary>Start a new process synchronously.</fsummary>
+ <desc>
+ <p>Starts a new process synchronously. Spawns the process and
+ waits for it to start. When the process has started, it
+ <em>must</em> call
+ <seemfa marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seemfa>
+ or <seemfa marker="#init_ack/1"><c>init_ack(Ret)</c></seemfa>,
+ where <c>Parent</c> is the process that evaluates this
+ function. At this time, <c>Ret</c> is returned.</p>
+ <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>Ret = {error, timeout}</c>
+ will be returned, and the process is killed.</p>
+ <p>Argument <c><anno>SpawnOpts</anno></c>, if specified, is passed
+ as the last argument to the <seemfa marker="erts:erlang#spawn_opt/2">
+ <c>spawn_opt/2,3,4,5</c></seemfa> BIF.</p>
<note>
<p>Using spawn option <c>monitor</c> is not
allowed. It causes the function to fail with reason
@@ -306,32 +327,71 @@ init(Parent) ->
</func>
<func>
- <name name="start" arity="3" since=""/>
- <name name="start" arity="4" since=""/>
- <name name="start" arity="5" since=""/>
<name name="start_link" arity="3" since=""/>
<name name="start_link" arity="4" since=""/>
<name name="start_link" arity="5" since=""/>
<fsummary>Start a new process synchronously.</fsummary>
<desc>
- <p>Starts a new process synchronously. Spawns the process and
- waits for it to start. When the process has started, it
+ <p>
+ Starts a new process synchronously. Spawns the process and
+ waits for it to start. A link is atomically set on the
+ newly spawned process. When the process has started, it
+ <em>must</em> call
+ <seemfa marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seemfa>
+ or <seemfa marker="#init_ack/1"><c>init_ack(Ret)</c></seemfa>,
+ where <c>Parent</c> is the process that evaluates this
+ function. At this time, <c>Ret</c> is returned.</p>
+ <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>Ret = {error, timeout}</c>
+ will be returned, and the process is killed.</p>
+ <p>If the process crashes before it has called <c>init_ack/1,2</c>,
+ <c>Ret = {error, <anno>Reason</anno>}</c> will be returned if
+ the calling process traps exits.</p>
+ <p>Argument <c><anno>SpawnOpts</anno></c>, if specified, is passed
+ as the last argument to the <seemfa marker="erts:erlang#spawn_opt/2">
+ <c>spawn_opt/2,3,4,5</c></seemfa> BIF.</p>
+ <note>
+ <p>Using spawn option <c>monitor</c> is not
+ allowed. It causes the function to fail with reason
+ <c>badarg</c>.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="start_monitor" arity="3" since="OTP 23.0"/>
+ <name name="start_monitor" arity="4" since="OTP 23.0"/>
+ <name name="start_monitor" arity="5" since="OTP 23.0"/>
+ <fsummary>Start a new process synchronously.</fsummary>
+ <desc>
+ <p>
+ Starts a new process synchronously. Spawns the process and
+ waits for it to start. A monitor is atomically set on the
+ newly spawned process. When the process has started, it
<em>must</em> call
- <seealso marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seealso>
- or <seealso marker="#init_ack/1"><c>init_ack(Ret)</c></seealso>,
+ <seemfa marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seemfa>
+ or <seemfa marker="#init_ack/1"><c>init_ack(Ret)</c></seemfa>,
where <c>Parent</c> is the process that evaluates this
function. At this time, <c>Ret</c> is returned.</p>
- <p>If function <c>start_link/3,4,5</c> is used and
- the process crashes before it has called <c>init_ack/1,2</c>,
- <c>{error, <anno>Reason</anno>}</c> is returned if the calling
- process traps exits.</p>
<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>
+ new process to call <c>init_ack</c>, or <c>Ret = {error, timeout}</c>
+ will be returned, and the process is killed.</p>
+ <p>
+ The return value is <c>{Ret, Mon}</c> where <c>Ret</c> corresponds
+ to the <c>Ret</c> argument in the call to <c>init_ack()</c>, and
+ <c>Mon</c> is the monitor reference of the monitor that has been
+ set up.
+ </p>
+ <p>
+ A <c>'DOWN'</c> message will be delivered to the caller if
+ this function returns, and the spawned process terminates. This is
+ true also in the case when the operation times out.
+ </p>
<p>Argument <c><anno>SpawnOpts</anno></c>, if specified, is passed
- as the last argument to the <seealso marker="erts:erlang#spawn_opt/2">
- <c>spawn_opt/2,3,4,5</c></seealso> BIF.</p>
+ as the last argument to the <seemfa marker="erts:erlang#spawn_opt/2">
+ <c>spawn_opt/2,3,4,5</c></seemfa> BIF.</p>
<note>
<p>Using spawn option <c>monitor</c> is not
allowed. It causes the function to fail with reason
@@ -345,8 +405,8 @@ init(Parent) ->
<fsummary>Terminate a process synchronously.</fsummary>
<type variable="Process"/>
<desc>
- <p>Equivalent to <seealso marker="#stop/3">
- <c>stop(Process, normal, infinity)</c></seealso>.</p>
+ <p>Equivalent to <seemfa marker="#stop/3">
+ <c>stop(Process, normal, infinity)</c></seemfa>.</p>
</desc>
</func>
@@ -368,9 +428,9 @@ init(Parent) ->
<c>terminate</c> system message, and requires that the
process handles system messages correctly.
For information about system messages, see
- <seealso marker="sys"><c>sys(3)</c></seealso> and section
- <seealso marker="doc/design_principles:spec_proc">
- sys and proc_lib</seealso> in OTP Design Principles.</p>
+ <seeerl marker="sys"><c>sys(3)</c></seeerl> and section
+ <seeguide marker="system/design_principles:spec_proc">
+ sys and proc_lib</seeguide> in OTP Design Principles.</p>
</desc>
</func>
@@ -380,8 +440,8 @@ init(Parent) ->
<c>proc_lib</c>spawned process.</fsummary>
<desc>
<p>This function is used by functions
- <seealso marker="c#i/0"><c>c:i/0</c></seealso> and
- <seealso marker="c#regs/0"><c>c:regs/0</c></seealso>
+ <seemfa marker="c#i/0"><c>c:i/0</c></seemfa> and
+ <seemfa marker="c#regs/0"><c>c:regs/0</c></seemfa>
to present process information.</p>
<p>This function extracts the initial call of a process that was
started using one of the spawn or start functions in this module,
@@ -414,10 +474,10 @@ init(Parent) ->
<section>
<title>See Also</title>
- <p><seealso marker="kernel:error_logger">
- <c>error_logger(3)</c></seealso></p>
- <p><seealso marker="kernel:logger">
- <c>logger(3)</c></seealso></p>
+ <p><seeerl marker="kernel:error_logger">
+ <c>error_logger(3)</c></seeerl></p>
+ <p><seeerl marker="kernel:logger">
+ <c>logger(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml
index 6dedaf79b5..f59b6eda17 100644
--- a/lib/stdlib/doc/src/proplists.xml
+++ b/lib/stdlib/doc/src/proplists.xml
@@ -70,7 +70,7 @@
<fsummary></fsummary>
<desc>
<p>Similar to
- <seealso marker="#get_all_values/2"><c>get_all_values/2</c></seealso>,
+ <seemfa marker="#get_all_values/2"><c>get_all_values/2</c></seemfa>,
but each value is wrapped in a list unless it is already itself a
list. The resulting list of lists is concatenated. This is often
useful for "incremental" options.</p>
@@ -90,8 +90,8 @@ append_values(a, [{a, [1,2]}, {b, 0}, {a, 3}, {c, -1}, {a, [4]}])</code>
<p>Minimizes the representation of all entries in the list. This is
equivalent to <c><![CDATA[[property(P) || P <- ListIn]]]></c>.</p>
<p>See also
- <seealso marker="#property/1"><c>property/1</c></seealso>,
- <seealso marker="#unfold/1"><c>unfold/1</c></seealso>.</p>
+ <seemfa marker="#property/1"><c>property/1</c></seemfa>,
+ <seemfa marker="#unfold/1"><c>unfold/1</c></seemfa>.</p>
</desc>
</func>
@@ -133,7 +133,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
<c><anno>Expansions</anno></c> contains more than one property with
the same key, only the first occurrence is used.</p>
<p>See also
- <seealso marker="#normalize/2"><c>normalize/2</c></seealso>.</p>
+ <seemfa marker="#normalize/2"><c>normalize/2</c></seemfa>.</p>
</desc>
</func>
@@ -142,7 +142,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
<fsummary></fsummary>
<desc>
<p>Similar to
- <seealso marker="#get_value/2"><c>get_value/2</c></seealso>,
+ <seemfa marker="#get_value/2"><c>get_value/2</c></seemfa>,
but returns the list of values for <em>all</em> entries
<c>{Key, Value}</c> in <c><anno>List</anno></c>. If no such entry
exists, the result is the empty list.</p>
@@ -158,8 +158,8 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
<c>{<anno>Key</anno>, true}</c>, this function returns <c>true</c>,
otherwise <c>false</c>.</p>
<p>See also
- <seealso marker="#get_value/2"><c>get_value/2</c></seealso>,
- <seealso marker="#lookup/2"><c>lookup/2</c></seealso>.</p>
+ <seemfa marker="#get_value/2"><c>get_value/2</c></seemfa>,
+ <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa>.</p>
</desc>
</func>
@@ -191,10 +191,10 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
this function returns the corresponding <c>Value</c>, otherwise
<c><anno>Default</anno></c>.</p>
<p>See also
- <seealso marker="#get_all_values/2"><c>get_all_values/2</c></seealso>,
- <seealso marker="#get_bool/2"><c>get_bool/2</c></seealso>,
- <seealso marker="#get_value/2"><c>get_value/2</c></seealso>,
- <seealso marker="#lookup/2"><c>lookup/2</c></seealso>.</p>
+ <seemfa marker="#get_all_values/2"><c>get_all_values/2</c></seemfa>,
+ <seemfa marker="#get_bool/2"><c>get_bool/2</c></seemfa>,
+ <seemfa marker="#get_value/2"><c>get_value/2</c></seemfa>,
+ <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa>.</p>
</desc>
</func>
@@ -217,9 +217,9 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
<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
- <seealso marker="#get_bool/2"><c>get_bool/2</c></seealso>,
- <seealso marker="#get_value/2"><c>get_value/2</c></seealso>,
- <seealso marker="#lookup_all/2"><c>lookup_all/2</c></seealso>.</p>
+ <seemfa marker="#get_bool/2"><c>get_bool/2</c></seemfa>,
+ <seemfa marker="#get_value/2"><c>get_value/2</c></seemfa>,
+ <seemfa marker="#lookup_all/2"><c>lookup_all/2</c></seemfa>.</p>
</desc>
</func>
@@ -231,7 +231,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
<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
- <seealso marker="#lookup/2"><c>lookup/2</c></seealso>.</p>
+ <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa>.</p>
</desc>
</func>
@@ -241,8 +241,8 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
<desc>
<p>Passes <c><anno>ListIn</anno></c> through a sequence of
substitution/expansion stages. For an <c>aliases</c> operation,
- function <seealso marker="#substitute_aliases/2">
- <c>substitute_aliases/2</c></seealso> is applied using the
+ function <seemfa marker="#substitute_aliases/2">
+ <c>substitute_aliases/2</c></seemfa> is applied using the
specified list of aliases:</p>
<list type="bulleted">
<item>
@@ -251,19 +251,19 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
</item>
<item>
<p>For an <c>expand</c> operation, function
- <seealso marker="#expand/2"><c>expand/2</c></seealso>
+ <seemfa marker="#expand/2"><c>expand/2</c></seemfa>
is applied using the specified list of expansions.</p>
</item>
</list>
<p>The final result is automatically compacted (compare
- <seealso marker="#compact/1"><c>compact/1</c></seealso>).</p>
+ <seemfa marker="#compact/1"><c>compact/1</c></seemfa>).</p>
<p>Typically you want to substitute negations first, then aliases,
then perform one or more expansions (sometimes you want to pre-expand
particular entries before doing the main expansion). You might want
to substitute negations and/or aliases repeatedly, to allow such
forms in the right-hand side of aliases and expansion lists.</p>
- <p>See also <seealso marker="#substitute_negations/2">
- <c>substitute_negations/2</c></seealso>.</p>
+ <p>See also <seemfa marker="#substitute_negations/2">
+ <c>substitute_negations/2</c></seemfa>.</p>
</desc>
</func>
@@ -276,7 +276,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
<c>Key</c> is an atom, <c>Key</c> is returned, otherwise
the whole term <c><anno>PropertyIn</anno></c> is returned.</p>
<p>See also
- <seealso marker="#property/2"><c>property/2</c></seealso>.</p>
+ <seemfa marker="#property/2"><c>property/2</c></seemfa>.</p>
</desc>
</func>
@@ -289,7 +289,7 @@ expand([{{foo, true}, [bar, baz]}], [{foo, false}, fie, foo, fum])</code>
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
- <seealso marker="#property/1"><c>property/1</c></seealso>.</p>
+ <seemfa marker="#property/1"><c>property/1</c></seemfa>.</p>
</desc>
</func>
@@ -330,9 +330,9 @@ split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</code>
with <c>{colour, ...}</c>, and all atoms <c>color</c>
with <c>colour</c>.</p>
<p>See also
- <seealso marker="#normalize/2"><c>normalize/2</c></seealso>,
- <seealso marker="#substitute_negations/2">
- <c>substitute_negations/2</c></seealso>.</p>
+ <seemfa marker="#normalize/2"><c>normalize/2</c></seemfa>,
+ <seemfa marker="#substitute_negations/2">
+ <c>substitute_negations/2</c></seemfa>.</p>
</desc>
</func>
@@ -348,8 +348,8 @@ split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</code>
<c>{K1, true}</c>, it is replaced with <c>{K2, false}</c>, otherwise
with <c>K2</c>, thus changing the name of the option and
simultaneously negating the value specified by
- <seealso marker="#get_bool/2">
- <c>get_bool(Key, <anno>ListIn</anno>)</c></seealso>.
+ <seemfa marker="#get_bool/2">
+ <c>get_bool(Key, <anno>ListIn</anno>)</c></seemfa>.
If the same <c>K1</c> occurs more than once in
<c><anno>Negations</anno></c>, only the first occurrence is used.</p>
<p>For example, <c>substitute_negations([{no_foo, foo}], L)</c>
@@ -357,10 +357,10 @@ split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</code>
<c>{no_foo, true}</c> in <c>L</c> with <c>{foo, false}</c>,
and any other tuple <c>{no_foo, ...}</c> with <c>foo</c>.</p>
<p>See also
- <seealso marker="#get_bool/2"><c>get_bool/2</c></seealso>,
- <seealso marker="#normalize/2"><c>normalize/2</c></seealso>,
- <seealso marker="#substitute_aliases/2">
- <c>substitute_aliases/2</c></seealso>.</p>
+ <seemfa marker="#get_bool/2"><c>get_bool/2</c></seemfa>,
+ <seemfa marker="#normalize/2"><c>normalize/2</c></seemfa>,
+ <seemfa marker="#substitute_aliases/2">
+ <c>substitute_aliases/2</c></seemfa>.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/qlc.xml b/lib/stdlib/doc/src/qlc.xml
index 34f7c5bab9..7c3c9e2e70 100644
--- a/lib/stdlib/doc/src/qlc.xml
+++ b/lib/stdlib/doc/src/qlc.xml
@@ -37,9 +37,9 @@
</modulesummary>
<description>
<p>This module provides a query interface to
- <seealso marker="mnesia:mnesia">Mnesia</seealso>,
- <seealso marker="ets">ETS</seealso>,
- <seealso marker="dets">Dets</seealso>,
+ <seeerl marker="mnesia:mnesia">Mnesia</seeerl>,
+ <seeerl marker="ets">ETS</seeerl>,
+ <seeerl marker="dets">Dets</seeerl>,
and other data structures that provide an iterator style
traversal of objects.</p>
</description>
@@ -49,41 +49,41 @@
<p>This module provides a query interface to <em>QLC
tables</em>. Typical QLC tables are Mnesia, ETS, and
Dets tables. Support is also provided for user-defined tables, see section
- <seealso marker="#implementing_a_qlc_table">
- Implementing a QLC Table</seealso>.
+ <seeerl marker="#implementing_a_qlc_table">
+ Implementing a QLC Table</seeerl>.
<marker id="query_list_comprehension"></marker>
A <em>query</em> is expressed 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
to ordinary list comprehensions as described in
- <seealso marker="doc/reference_manual:expressions#lcs">
- Erlang Reference Manual</seealso> and
- <seealso marker="doc/programming_examples:list_comprehensions">
- Programming Examples</seealso>, except that variables
+ <seeguide marker="system/reference_manual:expressions#lcs">
+ Erlang Reference Manual</seeguide> and
+ <seeguide marker="system/programming_examples:list_comprehensions">
+ Programming Examples</seeguide>, except that variables
introduced in patterns cannot be used in list expressions.
In the absence of optimizations and options such as
<c>cache</c> and <c>unique</c> (see section
- <seealso marker="#common_options">Common Options</seealso>, every
+ <seeerl marker="#common_options">Common Options</seeerl>, every
QLC free of QLC tables evaluates to the same list of answers as the
identical ordinary list comprehension.</p>
<p>While ordinary list comprehensions evaluate to lists, calling
- <seealso marker="#q/1"><c>q/1,2</c></seealso> returns a
+ <seemfa marker="#q/1"><c>q/1,2</c></seemfa> returns a
<marker id="query_handle"></marker><em>query handle</em>.
- To obtain all the answers to a query, <seealso marker="#eval/1">
- <c>eval/1,2</c></seealso> is to be called with the
+ To obtain all the answers to a query, <seemfa marker="#eval/1">
+ <c>eval/1,2</c></seemfa> is to be called with the
query handle as first argument. Query handles are essentially
functional objects (funs) created in the module calling <c>q/1,2</c>.
As the funs refer to the module code, be careful not to keep query
handles too long if the module code is to be replaced.
Code replacement is described in section
- <seealso marker="doc/reference_manual:code_loading">
- Compilation and Code Loading</seealso> in the Erlang Reference Manual.
+ <seeguide marker="system/reference_manual:code_loading">
+ Compilation and Code Loading</seeguide> in the Erlang Reference Manual.
The list of answers can also be traversed in chunks by use of a
<marker id="query_cursor"></marker><em>query cursor</em>.
Query cursors are created by calling
- <seealso marker="#cursor/1"><c>cursor/1,2</c></seealso> with a query
+ <seemfa marker="#cursor/1"><c>cursor/1,2</c></seemfa> with a query
handle as first argument. Query cursors are essentially Erlang processes.
One answer at a time is sent from the query cursor process to
the process that created the cursor.</p>
@@ -104,13 +104,13 @@
<c><![CDATA[Pattern <- ListExpression]]></c>, where
<c>ListExpression</c> is an expression evaluating to a query
handle or a list. Query handles are returned from
- <seealso marker="#append/1"><c>append/1,2</c></seealso>,
- <seealso marker="#keysort/2"><c>keysort/2,3</c></seealso>,
- <seealso marker="#q/1"><c>q/1,2</c></seealso>,
- <seealso marker="#sort/1"><c>sort/1,2</c></seealso>,
- <seealso marker="#string_to_handle/1">
- <c>string_to_handle/1,2,3</c></seealso>, and
- <seealso marker="#table/2"><c>table/2</c></seealso>.</p>
+ <seemfa marker="#append/1"><c>append/1,2</c></seemfa>,
+ <seemfa marker="#keysort/2"><c>keysort/2,3</c></seemfa>,
+ <seemfa marker="#q/1"><c>q/1,2</c></seemfa>,
+ <seemfa marker="#sort/1"><c>sort/1,2</c></seemfa>,
+ <seemfa marker="#string_to_handle/1">
+ <c>string_to_handle/1,2,3</c></seemfa>, and
+ <seemfa marker="#table/2"><c>table/2</c></seemfa>.</p>
</section>
<section>
@@ -200,10 +200,10 @@
<marker id="common_options"></marker>
<title>Common Options</title>
<p>The following options are accepted by
- <seealso marker="#cursor/2"><c>cursor/2</c></seealso>,
- <seealso marker="#eval/2"><c>eval/2</c></seealso>,
- <seealso marker="#fold/4"><c>fold/4</c></seealso>, and
- <seealso marker="#info/2"><c>info/2</c></seealso>:</p>
+ <seemfa marker="#cursor/2"><c>cursor/2</c></seemfa>,
+ <seemfa marker="#eval/2"><c>eval/2</c></seemfa>,
+ <seemfa marker="#fold/4"><c>fold/4</c></seemfa>, and
+ <seemfa marker="#info/2"><c>info/2</c></seemfa>:</p>
<list type="bulleted">
<item><p><c>{cache_all, Cache}</c>, where <c>Cache</c> is
@@ -230,15 +230,15 @@
The values <c>info_msg</c>, <c>warning_msg</c>, and
<c>error_msg</c> mean that the function with the corresponding
name in module
- <seealso marker="kernel:error_logger"><c>error_logger</c></seealso>
+ <seeerl marker="kernel:error_logger"><c>error_logger</c></seeerl>
is called for printing some information (currently the stacktrace).</p>
</item>
<item><p><c>{tmpdir, TempDirectory}</c> sets the directory used by
merge join for temporary files and by option
<c>{cache,&nbsp;list}</c>. The option also overrides
option <c>tmpdir</c> of
- <seealso marker="#keysort/3"><c>keysort/3</c></seealso> and
- <seealso marker="#sort/2"><c>sort/2</c></seealso>.
+ <seemfa marker="#keysort/3"><c>keysort/3</c></seemfa> and
+ <seemfa marker="#sort/2"><c>sort/2</c></seemfa>.
Defaults to <c>""</c>, which means that
the directory returned by <c>file:get_cwd()</c> is used.</p>
</item>
@@ -257,11 +257,11 @@
<p>As mentioned earlier,
queries are expressed in the list comprehension syntax as described
in section
- <seealso marker="doc/reference_manual:expressions">Expressions</seealso>
+ <seeguide marker="system/reference_manual:expressions">Expressions</seeguide>
in Erlang Reference Manual. In the following, some familiarity
with list comprehensions is assumed. The examples in section
- <seealso marker="doc/programming_examples:list_comprehensions">
- List Comprehensions</seealso> in Programming Examples can get you
+ <seeguide marker="system/programming_examples:list_comprehensions">
+ List Comprehensions</seeguide> in Programming Examples can get you
started. Notice that list comprehensions do not add any computational
power to the language; anything that can be done with list
comprehensions can also be done without them. But they add
@@ -277,7 +277,7 @@
<c>Y</c> is introduced in the first generator and used in the second.
The ordinary list comprehension is normally to be preferred when
there is a choice as to which to use. One difference is that
- <seealso marker="#eval/1"><c>eval/1,2</c></seealso>
+ <seemfa marker="#eval/1"><c>eval/1,2</c></seemfa>
collects answers in a list that is finally
reversed, while list comprehensions collect answers on the stack
that is finally unwound.</p>
@@ -285,26 +285,26 @@
<p>What the <c>qlc</c> module primarily adds to list
comprehensions is that data can be read from QLC tables in small
chunks. A QLC table is created by calling
- <seealso marker="#table/2"><c>qlc:table/2</c></seealso>.
+ <seemfa marker="#table/2"><c>qlc:table/2</c></seemfa>.
Usually <c>qlc:table/2</c> is not called directly from the query
but through an interface function of some data structure.
Erlang/OTP includes a few examples of such functions:
- <seealso marker="mnesia:mnesia#table/1"><c>mnesia:table/1,2</c></seealso>,
- <seealso marker="ets#table/1"><c>ets:table/1,2</c></seealso>, and
- <seealso marker="dets#table/1"><c>dets:table/1,2</c></seealso>.
+ <seemfa marker="mnesia:mnesia#table/1"><c>mnesia:table/1,2</c></seemfa>,
+ <seemfa marker="ets#table/1"><c>ets:table/1,2</c></seemfa>, and
+ <seemfa marker="dets#table/1"><c>dets:table/1,2</c></seemfa>.
For a given data structure, many functions can create QLC tables, but
common for these functions is that they return a query handle created by
- <seealso marker="#table/2"><c>qlc:table/2</c></seealso>.
+ <seemfa marker="#table/2"><c>qlc:table/2</c></seemfa>.
Using the QLC tables provided by Erlang/OTP is usually
probably sufficient, but for the more advanced user section
- <seealso marker="#implementing_a_qlc_table">Implementing a QLC
- Table</seealso> describes the implementation of a function
+ <seeerl marker="#implementing_a_qlc_table">Implementing a QLC
+ Table</seeerl> describes the implementation of a function
calling <c>qlc:table/2</c>.</p>
<p>Besides <c>qlc:table/2</c>, other functions
return query handles. They are used more seldom than tables,
but are sometimes useful.
- <seealso marker="#append/1"><c>qlc:append/1,2</c></seealso> traverses
+ <seemfa marker="#append/1"><c>qlc:append/1,2</c></seemfa> traverses
objects from many tables or lists after each other. If, for
example, you want to traverse all answers to a query <c>QH</c> and
then finish off by a term <c>{finished}</c>, you can do that by
@@ -389,9 +389,9 @@ R.]]></code>
the filter is run M*N times.</p>
<p>If <c>QH2</c> is a call to the function for
- <seealso marker="gb_trees"><c>gb_trees</c></seealso>, as defined
- in section <seealso marker="#implementing_a_qlc_table">Implementing
- a QLC Table</seealso>, then <c>gb_table:table/1</c>, the
+ <seeerl marker="gb_trees"><c>gb_trees</c></seeerl>, as defined
+ in section <seeerl marker="#implementing_a_qlc_table">Implementing
+ a QLC Table</seeerl>, then <c>gb_table:table/1</c>, the
iterator for the gb-tree is initiated for each answer to
<c>QH1</c>. The objects of the gb-tree are then returned one
by one. This is probably the most efficient way of traversing
@@ -432,8 +432,8 @@ R.]]></code>
lists is that if the list size exceeds a limit, a
temporary file is used. Reading the answers from a file is
much slower than copying them from an ETS table. But if the
- available RAM memory is scarce, setting the <seealso
- marker="#max_list_size">limit</seealso> to some low value is an
+ available RAM memory is scarce, setting the <seeerl
+ marker="#max_list_size">limit</seeerl> to some low value is an
alternative.</p>
<p>Option <c>cache_all</c> can be set to
@@ -449,9 +449,9 @@ R.]]></code>
<marker id="implementing_a_qlc_table"></marker>
<title>Implementing a QLC Table</title>
<p>As an example of
- how to use function <seealso marker="#table/2"><c>table/2</c></seealso>,
- the implementation of a QLC table for the <seealso
- marker="gb_trees"><c>gb_trees</c></seealso> module is given:</p>
+ how to use function <seemfa marker="#table/2"><c>table/2</c></seemfa>,
+ the implementation of a QLC table for the <seeerl
+ marker="gb_trees"><c>gb_trees</c></seeerl> module is given:</p>
<code type="none">
<![CDATA[-module(gb_table).
@@ -529,7 +529,7 @@ gb_iter(I0, N, EFun) ->
<c>{Key,&nbsp;Value}</c> pairs, as the traversal function does.</p>
<p>The format function is also optional. It is called by
- <seealso marker="#info/1"><c>info/1,2</c></seealso>
+ <seemfa marker="#info/1"><c>info/1,2</c></seemfa>
to give feedback at runtime of how the query
is to be evaluated. Try to give as good feedback as
possible without showing too much details. In the example, at
@@ -593,8 +593,8 @@ ets:match_spec_run(
exactly as <c>=:=/2</c> would have been handled. However,
if it cannot be determined at compile time that some
constant is free of integers, and the table uses <c>=:=/2</c>
- when comparing keys for equality (see option <seealso
- marker="#key_equality">key_equality</seealso>), then the
+ when comparing keys for equality (see option <seeerl
+ marker="#key_equality">key_equality</seeerl>), then the
<c>qlc</c> module does not try to look up the constant. The
reason is that there is in the general case no upper limit on
the number of key values that can compare equal to such a
@@ -648,8 +648,8 @@ ets:match_spec_run(ets:lookup(#Ref&lt;0.3098908599.2283929601.256211>,
<datatypes>
<datatype>
<name name="abstract_expr"></name>
- <desc><p>Parse trees for Erlang expression, see section <seealso
- marker="erts:absform">The Abstract Format</seealso>
+ <desc><p>Parse trees for Erlang expression, see section <seeguide
+ marker="erts:absform">The Abstract Format</seeguide>
in the ERTS User's Guide.</p></desc>
</datatype>
<datatype>
@@ -663,10 +663,10 @@ ets:match_spec_run(ets:lookup(#Ref&lt;0.3098908599.2283929601.256211>,
</datatype>
<datatype>
<name name="match_expression"></name>
- <desc><p>Match&nbsp;specification, see section <seealso
- marker="erts:match_spec">Match Specifications in Erlang</seealso>
- in the ERTS User's Guide and <seealso
- marker="ms_transform"><c>ms_transform(3)</c></seealso>.</p></desc>
+ <desc><p>Match&nbsp;specification, see section <seeguide
+ marker="erts:match_spec">Match Specifications in Erlang</seeguide>
+ in the ERTS User's Guide and <seeerl
+ marker="ms_transform"><c>ms_transform(3)</c></seeerl>.</p></desc>
</datatype>
<datatype>
<name name="no_files"></name>
@@ -686,12 +686,12 @@ ets:match_spec_run(ets:lookup(#Ref&lt;0.3098908599.2283929601.256211>,
</datatype>
<datatype>
<name name="query_cursor"></name>
- <desc><p>A <seealso marker="#query_cursor">query cursor</seealso>.</p>
+ <desc><p>A <seeerl marker="#query_cursor">query cursor</seeerl>.</p>
</desc>
</datatype>
<datatype>
<name name="query_handle"></name>
- <desc><p>A <seealso marker="#query_handle">query handle</seealso>.</p>
+ <desc><p>A <seeerl marker="#query_handle">query handle</seeerl>.</p>
</desc>
</datatype>
<datatype>
@@ -700,8 +700,8 @@ ets:match_spec_run(ets:lookup(#Ref&lt;0.3098908599.2283929601.256211>,
<datatype>
<name name="query_list_comprehension"></name>
<desc><p>A literal
- <seealso marker="#query_list_comprehension">query
- list comprehension</seealso>.</p></desc>
+ <seeerl marker="#query_list_comprehension">query
+ list comprehension</seeerl>.</p></desc>
</datatype>
<datatype>
<name name="spawn_options"></name>
@@ -711,8 +711,8 @@ ets:match_spec_run(ets:lookup(#Ref&lt;0.3098908599.2283929601.256211>,
</datatype>
<datatype>
<name name="sort_option"></name>
- <desc><p>See <seealso
- marker="file_sorter"><c>file_sorter(3)</c></seealso>.</p></desc>
+ <desc><p>See <seeerl
+ marker="file_sorter"><c>file_sorter(3)</c></seeerl>.</p></desc>
</datatype>
<datatype>
<name name="tmp_directory"></name>
@@ -755,11 +755,11 @@ ets:match_spec_run(ets:lookup(#Ref&lt;0.3098908599.2283929601.256211>,
<p>Creates a query cursor and
makes the calling process the owner of the cursor. The
cursor is to be used as argument to
- <seealso marker="#next_answers/1">
- <c>next_answers/1,2</c></seealso> and (eventually)
- <seealso marker="#delete_cursor/1"><c>delete_cursor/1</c></seealso>.
- Calls <seealso marker="erts:erlang#spawn_opt/2">
- <c>erlang:spawn_opt/2</c></seealso> to spawn and link to
+ <seemfa marker="#next_answers/1">
+ <c>next_answers/1,2</c></seemfa> and (eventually)
+ <seemfa marker="#delete_cursor/1"><c>delete_cursor/1</c></seemfa>.
+ Calls <seemfa marker="erts:erlang#spawn_opt/2">
+ <c>erlang:spawn_opt/2</c></seemfa> to spawn and link to
a process that evaluates the query handle. The value of option
<c>spawn_options</c> is used as last argument when calling
<c>spawn_opt/2</c>. Defaults to <c>[link]</c>.</p>
@@ -958,8 +958,8 @@ end</pre>
<desc>
<p>Returns a query handle. When evaluating query handle
<c><anno>QH2</anno></c>, the answers to query handle
- <c><anno>QH1</anno></c> are sorted by <seealso
- marker="file_sorter#keysort/4"><c>file_sorter:keysort/4</c></seealso>
+ <c><anno>QH1</anno></c> are sorted by <seemfa
+ marker="file_sorter#keysort/4"><c>file_sorter:keysort/4</c></seemfa>
according to the options.</p>
<p>The sorter uses temporary files only if
<c><anno>QH1</anno></c> does not evaluate to a list and the
@@ -1004,7 +1004,7 @@ end</pre>
<p>When calling <c>qlc:q/1,2</c> from the Erlang shell, the
parse transform is automatically called. When this occurs, the fun
substituted for the QLC is not compiled but is evaluated by
- <seealso marker="erl_eval"><c>erl_eval(3)</c></seealso>. This
+ <seeerl marker="erl_eval"><c>erl_eval(3)</c></seeerl>. This
is also true when expressions are evaluated by
<c>file:eval/1,2</c> or in the debugger.</p>
<p>To be explicit, this does not work:</p>
@@ -1062,7 +1062,7 @@ QH = qlc:q(A),
table only. If option <c>unique</c> is combined with option
<c>{cache,&nbsp;list}</c>, the answers are sorted
twice using
- <seealso marker="#keysort/3"><c>keysort/3</c></seealso>;
+ <seemfa marker="#keysort/3"><c>keysort/3</c></seemfa>;
once to remove duplicates and once to restore the order.</p>
</item>
</list>
@@ -1110,14 +1110,14 @@ begin
X =:= Y
])
end</pre>
- <p><seealso marker="#sort/1"><c>sort/1,2</c></seealso> and
- <seealso marker="#keysort/2"><c>keysort/2,3</c></seealso>
+ <p><seemfa marker="#sort/1"><c>sort/1,2</c></seemfa> and
+ <seemfa marker="#keysort/2"><c>keysort/2,3</c></seemfa>
can also be used for
caching answers and for removing duplicates. When sorting
answers are cached in a list, possibly stored on a temporary
file, and no ETS tables are used.</p>
- <p>Sometimes (see <seealso
- marker="#table/2"><c>table/2</c></seealso>) traversal
+ <p>Sometimes (see <seemfa
+ marker="#table/2"><c>table/2</c></seemfa>) traversal
of tables can be done by looking up key values, which is
assumed to be fast. Under certain (rare) circumstances
there can be too many key values to look up.
@@ -1130,8 +1130,8 @@ end</pre>
number of keys to look up.</p>
<p><em>Example:</em></p>
<p>In the following example, using the <c>gb_table</c> module from
- section <seealso marker="#implementing_a_qlc_table">Implementing a
- QLC Table</seealso>, there are six keys to look up:
+ section <seeerl marker="#implementing_a_qlc_table">Implementing a
+ QLC Table</seeerl>, there are six keys to look up:
<c>{1,a}</c>, <c>{1,b}</c>, <c>{1,c}</c>, <c>{2,a}</c>,
<c>{2,b}</c>, and <c>{2,c}</c>. The reason is that the two
elements of key <c>{X,&nbsp;Y}</c> are compared separately.</p>
@@ -1207,8 +1207,8 @@ ets:match_spec_run(
<desc>
<p>Returns a query handle. When evaluating query handle
<c><anno>QH2</anno></c>, the answers to query handle
- <c><anno>QH1</anno></c> are sorted by <seealso
- marker="file_sorter#sort/3"><c>file_sorter:sort/3</c></seealso>
+ <c><anno>QH1</anno></c> are sorted by <seemfa
+ marker="file_sorter#sort/3"><c>file_sorter:sort/3</c></seemfa>
according to the options.</p>
<p>The sorter uses temporary files only if
<c><anno>QH1</anno></c> does not evaluate to a list and the
@@ -1226,10 +1226,10 @@ ets:match_spec_run(
<name name="string_to_handle" arity="3" since=""/>
<fsummary>Return a handle for a query list comprehension.</fsummary>
<desc>
- <p>A string version of <seealso marker="#q/1"><c>q/1,2</c></seealso>.
+ <p>A string version of <seemfa marker="#q/1"><c>q/1,2</c></seemfa>.
When the query handle is evaluated, the fun created by the parse
transform is interpreted by
- <seealso marker="erl_eval"><c>erl_eval(3)</c></seealso>.
+ <seeerl marker="erl_eval"><c>erl_eval(3)</c></seeerl>.
The query string is to be one single QLC terminated by a period.</p>
<p><em>Example:</em></p>
<pre>
@@ -1283,15 +1283,15 @@ ets:match_spec_run(
<p>Modules that can use match specifications for optimized
traversal of tables are to call <c>qlc:table/2</c> with an unary
<c><anno>TraverseFun</anno></c>. An example is
- <seealso marker="ets#table/2">
- <c>ets:table/2</c></seealso>.</p>
+ <seemfa marker="ets#table/2">
+ <c>ets:table/2</c></seemfa>.</p>
</item>
<item>
<p>Other modules can provide a nullary
<c><anno>TraverseFun</anno></c>. An example is
<c>gb_table:table/1</c> in section
- <seealso marker="#implementing_a_qlc_table">Implementing a
- QLC Table</seealso>.</p>
+ <seeerl marker="#implementing_a_qlc_table">Implementing a
+ QLC Table</seeerl>.</p>
</item>
</list>
</item>
@@ -1310,9 +1310,9 @@ ets:match_spec_run(
<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
- <seealso marker="#eval/1"><c>eval/1,2</c></seealso>,
- <seealso marker="#fold/3"><c>fold/3,4</c></seealso>, or
- <seealso marker="#cursor/1"><c>cursor/1,2</c></seealso>.
+ <seemfa marker="#eval/1"><c>eval/1,2</c></seemfa>,
+ <seemfa marker="#fold/3"><c>fold/3,4</c></seemfa>, or
+ <seemfa marker="#cursor/1"><c>cursor/1,2</c></seemfa>.
</p>
</item>
<item>
@@ -1361,7 +1361,7 @@ ets:match_spec_run(
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>
+ more than <seeerl marker="#max_lookup">max_lookup</seeerl>
lookups are ignored.</p>
</item>
<item>
@@ -1391,7 +1391,7 @@ ets:match_spec_run(
</item>
<item>
<p>Unary callback function <c><anno>FormatFun</anno></c>
- is used by <seealso marker="#info/1"><c>info/1,2</c></seealso>
+ is used by <seemfa marker="#info/1"><c>info/1,2</c></seemfa>
for displaying the call that created the query handle of the
table. Defaults to <c>undefined</c>, which means that
<c>info/1,2</c> displays a call to <c>'$MOD':'$FUN'/0</c>.
@@ -1448,9 +1448,9 @@ ets:match_spec_run(
</list>
<p>For the various options recognized by <c>table/1,2</c>
in respective module, see
- <seealso marker="ets#table/1"><c>ets(3)</c></seealso>,
- <seealso marker="dets#table/1"><c>dets(3)</c></seealso>, and
- <seealso marker="mnesia:mnesia#table/1"><c>mnesia(3)</c></seealso>.
+ <seemfa marker="ets#table/1"><c>ets(3)</c></seemfa>,
+ <seemfa marker="dets#table/1"><c>dets(3)</c></seemfa>, and
+ <seemfa marker="mnesia:mnesia#table/1"><c>mnesia(3)</c></seemfa>.
</p>
</desc>
</func>
@@ -1458,18 +1458,18 @@ ets:match_spec_run(
<section>
<title>See Also</title>
- <p><seealso marker="dets"><c>dets(3)</c></seealso>,
- <seealso marker="erl_eval"><c>erl_eval(3)</c></seealso>,
- <seealso marker="erts:erlang"><c>erlang(3)</c></seealso>,
- <seealso marker="kernel:error_logger"><c>error_logger(3)</c></seealso>,
- <seealso marker="ets"><c>ets(3)</c></seealso>,
- <seealso marker="kernel:file"><c>file(3)</c></seealso>,
- <seealso marker="file_sorter"><c>file_sorter(3)</c></seealso>,
- <seealso marker="mnesia:mnesia"><c>mnesia(3)</c></seealso>,
- <seealso marker="shell"><c>shell(3)</c></seealso>,
- <seealso marker="doc/reference_manual:users_guide">
- Erlang Reference Manual</seealso>,
- <seealso marker="doc/programming_examples:users_guide">
- Programming Examples</seealso></p>
+ <p><seeerl marker="dets"><c>dets(3)</c></seeerl>,
+ <seeerl marker="erl_eval"><c>erl_eval(3)</c></seeerl>,
+ <seeerl marker="erts:erlang"><c>erlang(3)</c></seeerl>,
+ <seeerl marker="kernel:error_logger"><c>error_logger(3)</c></seeerl>,
+ <seeerl marker="ets"><c>ets(3)</c></seeerl>,
+ <seeerl marker="kernel:file"><c>file(3)</c></seeerl>,
+ <seeerl marker="file_sorter"><c>file_sorter(3)</c></seeerl>,
+ <seeerl marker="mnesia:mnesia"><c>mnesia(3)</c></seeerl>,
+ <seeerl marker="shell"><c>shell(3)</c></seeerl>,
+ <seeguide marker="system/reference_manual:index">
+ Erlang Reference Manual</seeguide>,
+ <seeguide marker="system/programming_examples:index">
+ Programming Examples</seeguide></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml
index ef0ce5681f..69b66129fa 100644
--- a/lib/stdlib/doc/src/queue.xml
+++ b/lib/stdlib/doc/src/queue.xml
@@ -53,11 +53,11 @@
assuming knowledge of the format is running on thin ice.</p>
<p>All operations have an amortized O(1) running time, except
- <seealso marker="#filter/2"><c>filter/2</c></seealso>,
- <seealso marker="#join/2"><c>join/2</c></seealso>,
- <seealso marker="#len/1"><c>len/1</c></seealso>,
- <seealso marker="#member/2"><c>member/2</c></seealso>,
- <seealso marker="#split/2"><c>split/2</c></seealso> that have O(n).
+ <seemfa marker="#filter/2"><c>filter/2</c></seemfa>,
+ <seemfa marker="#join/2"><c>join/2</c></seemfa>,
+ <seemfa marker="#len/1"><c>len/1</c></seemfa>,
+ <seemfa marker="#member/2"><c>member/2</c></seemfa>,
+ <seemfa marker="#split/2"><c>split/2</c></seemfa> that have O(n).
To minimize the size of a queue minimizing
the amount of garbage built by queue operations, the queues
do not contain explicit length information, and that is
@@ -96,15 +96,13 @@
some with more readable but perhaps less understandable aliases.</p>
</description>
- <section>
- <title>Original API</title>
- </section>
+
<datatypes>
<datatype>
<name name="queue" n_vars="1"/>
<desc><p>As returned by
- <seealso marker="#new/0"><c>new/0</c></seealso>.</p></desc>
+ <seemfa marker="#new/0"><c>new/0</c></seemfa>.</p></desc>
</datatype>
<datatype>
<name name="queue" n_vars="0"/>
@@ -112,6 +110,9 @@
</datatypes>
<funcs>
+ <fsdescription>
+ <title>Original API</title>
+ </fsdescription>
<func>
<name name="filter" arity="2" since=""/>
<fsummary>Filter a queue.</fsummary>
@@ -270,11 +271,12 @@
</func>
</funcs>
- <section>
- <title>Extended API</title>
- </section>
+
<funcs>
+ <fsdescription>
+ <title>Extended API</title>
+ </fsdescription>
<func>
<name name="drop" arity="1" since=""/>
<fsummary>Remove the front item from a queue.</fsummary>
@@ -336,11 +338,12 @@
</func>
</funcs>
- <section>
- <title>Okasaki API</title>
- </section>
+
<funcs>
+ <fsdescription>
+ <title>Okasaki API</title>
+ </fsdescription>
<func>
<name name="cons" arity="2" since=""/>
<fsummary>Insert an item at the head of a queue.</fsummary>
diff --git a/lib/stdlib/doc/src/rand.xml b/lib/stdlib/doc/src/rand.xml
index 5ed67dcaca..ac99736761 100644
--- a/lib/stdlib/doc/src/rand.xml
+++ b/lib/stdlib/doc/src/rand.xml
@@ -131,8 +131,8 @@
<p>
The default algorithm is <c>exsss</c> (Xorshift116**).
If a specific algorithm is
- required, ensure to always use <seealso marker="#seed-1">
- <c>seed/1</c></seealso> to initialize the state.
+ required, ensure to always use <seemfa marker="#seed/1">
+ <c>seed/1</c></seemfa> to initialize the state.
</p>
<p>
@@ -179,10 +179,10 @@
variable <c>rand_seed</c> to remember the current state.</p>
<p>If a process calls
- <seealso marker="#uniform-0"><c>uniform/0</c></seealso>,
- <seealso marker="#uniform-1"><c>uniform/1</c></seealso> or
- <seealso marker="#uniform_real-0"><c>uniform_real/0</c></seealso> without
- setting a seed first, <seealso marker="#seed-1"><c>seed/1</c></seealso>
+ <seemfa marker="#uniform/0"><c>uniform/0</c></seemfa>,
+ <seemfa marker="#uniform/1"><c>uniform/1</c></seemfa> or
+ <seemfa marker="#uniform_real/0"><c>uniform_real/0</c></seemfa> without
+ setting a seed first, <seemfa marker="#seed/1"><c>seed/1</c></seemfa>
is called automatically with the default algorithm and creates a
non-constant seed.</p>
@@ -236,7 +236,7 @@ SND0 = math:sqrt(-2 * math:log(R5)) * math:cos(math:pi() * R6)</pre>
<p>The builtin random number generator algorithms are not
cryptographically strong. If a cryptographically strong
random number generator is needed, use something like
- <seealso marker="crypto:crypto#rand_seed-0"><c>crypto:rand_seed/0</c></seealso>.
+ <seemfa marker="crypto:crypto#rand_seed/0"><c>crypto:rand_seed/0</c></seemfa>.
</p>
</note>
@@ -353,7 +353,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre>
<fsummary>Export the random number generation state.</fsummary>
<desc><marker id="export_seed-0"/>
<p>Returns the random number state in an external format.
- To be used with <seealso marker="#seed-1"><c>seed/1</c></seealso>.</p>
+ To be used with <seemfa marker="#seed/1"><c>seed/1</c></seemfa>.</p>
</desc>
</func>
@@ -362,7 +362,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre>
<fsummary>Export the random number generation state.</fsummary>
<desc><marker id="export_seed_s-1"/>
<p>Returns the random number generator state in an external format.
- To be used with <seealso marker="#seed-1"><c>seed/1</c></seealso>.</p>
+ To be used with <seemfa marker="#seed/1"><c>seed/1</c></seemfa>.</p>
</desc>
</func>
@@ -443,7 +443,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre>
</p>
<p>Otherwise recreates the exported seed in the process dictionary,
and returns the state. See also
- <seealso marker="#export_seed-0"><c>export_seed/0</c></seealso>.</p>
+ <seemfa marker="#export_seed/0"><c>export_seed/0</c></seemfa>.</p>
</desc>
</func>
@@ -466,8 +466,8 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre>
is an algorithm.
</p>
<p>Otherwise recreates the exported seed and returns the state.
- See also <seealso marker="#export_seed-0">
- <c>export_seed/0</c></seealso>.</p>
+ See also <seemfa marker="#export_seed/0">
+ <c>export_seed/0</c></seemfa>.</p>
</desc>
</func>
@@ -499,7 +499,7 @@ tests. We suggest to use a sign test to extract a random Boolean value.</pre>
fatal for certain applications. If that is undesired
you can use <c>(1.0 - rand:uniform())</c> to get the
interval <c>0.0 &lt; <anno>X</anno> =&lt; 1.0</c>, or instead use
- <seealso marker="#uniform_real-0"><c>uniform_real/0</c></seealso>.
+ <seemfa marker="#uniform_real/0"><c>uniform_real/0</c></seemfa>.
</p>
<p>
If neither endpoint is desired you can test and re-try
@@ -537,7 +537,7 @@ end.</pre>
<p>
The generated numbers from this function has got better
granularity for small numbers than the regular
- <seealso marker="#uniform-0"><c>uniform/0</c></seealso>
+ <seemfa marker="#uniform/0"><c>uniform/0</c></seemfa>
because all bits in the mantissa are random.
This property, in combination with the fact that exactly zero
is never returned is useful for algoritms doing for example
@@ -546,7 +546,7 @@ end.</pre>
</note>
<p>
See
- <seealso marker="#uniform_real_s-1"><c>uniform_real_s/1</c></seealso>
+ <seemfa marker="#uniform_real_s/1"><c>uniform_real_s/1</c></seemfa>
for more explanation.
</p>
</desc>
@@ -582,7 +582,7 @@ end.</pre>
fatal for certain applications. If that is undesired
you can use <c>(1.0 - rand:uniform(State))</c> to get the
interval <c>0.0 &lt; <anno>X</anno> =&lt; 1.0</c>, or instead use
- <seealso marker="#uniform_real_s-1"><c>uniform_real_s/1</c></seealso>.
+ <seemfa marker="#uniform_real_s/1"><c>uniform_real_s/1</c></seemfa>.
</p>
<p>
If neither endpoint is desired you can test and re-try
@@ -620,7 +620,7 @@ end.</pre>
<p>
The generated numbers from this function has got better
granularity for small numbers than the regular
- <seealso marker="#uniform_s-1"><c>uniform_s/1</c></seealso>
+ <seemfa marker="#uniform_s/1"><c>uniform_s/1</c></seemfa>
because all bits in the mantissa are random.
This property, in combination with the fact that exactly zero
is never returned is useful for algoritms doing for example
@@ -650,13 +650,13 @@ end.</pre>
<c>0 =&lt; integer(N) &lt; 2.0^53</c>
the probability is the same.
Compare that with the form of the numbers generated by
- <seealso marker="#uniform_s-1"><c>uniform_s/1</c></seealso>.
+ <seemfa marker="#uniform_s/1"><c>uniform_s/1</c></seemfa>.
</p>
<p>
Having to generate extra random bits for
small numbers costs a little performance.
This function is about 20% slower than the regular
- <seealso marker="#uniform_s-1"><c>uniform_s/1</c></seealso>
+ <seemfa marker="#uniform_s/1"><c>uniform_s/1</c></seemfa>
</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/random.xml b/lib/stdlib/doc/src/random.xml
index 7566be0aab..30197d31fc 100644
--- a/lib/stdlib/doc/src/random.xml
+++ b/lib/stdlib/doc/src/random.xml
@@ -53,13 +53,13 @@
<p>This random number generator is not cryptographically
strong. If a strong cryptographic random number generator is
needed, use one of functions in the
- <seealso marker="crypto:crypto"><c>crypto</c></seealso>
- module, for example, <seealso marker="crypto:crypto">
- <c>crypto:strong_rand_bytes/1</c></seealso>.</p>
+ <seeerl marker="crypto:crypto"><c>crypto</c></seeerl>
+ module, for example, <seeerl marker="crypto:crypto">
+ <c>crypto:strong_rand_bytes/1</c></seeerl>.</p>
</note>
<note>
- <p>The improved <seealso marker="rand"><c>rand</c></seealso>
+ <p>The improved <seeerl marker="rand"><c>rand</c></seeerl>
module is to be used instead of this module.</p>
</note>
</description>
@@ -104,14 +104,14 @@ random:seed(erlang:phash2([node()]),
erlang:monotonic_time(),
erlang:unique_integer())</code>
<p>For details, see
- <seealso marker="erts:erlang#phash2/1">
- <c>erlang:phash2/1</c></seealso>,
- <seealso marker="erts:erlang#node/0">
- <c>erlang:node/0</c></seealso>,
- <seealso marker="erts:erlang#monotonic_time/0">
- <c>erlang:monotonic_time/0</c></seealso>, and
- <seealso marker="erts:erlang#unique_integer/0">
- <c>erlang:unique_integer/0</c></seealso>.</p>
+ <seemfa marker="erts:erlang#phash2/1">
+ <c>erlang:phash2/1</c></seemfa>,
+ <seemfa marker="erts:erlang#node/0">
+ <c>erlang:node/0</c></seemfa>,
+ <seemfa marker="erts:erlang#monotonic_time/0">
+ <c>erlang:monotonic_time/0</c></seemfa>, and
+ <seemfa marker="erts:erlang#unique_integer/0">
+ <c>erlang:unique_integer/0</c></seemfa>.</p>
</desc>
</func>
@@ -169,10 +169,10 @@ random:seed(erlang:phash2([node()]),
<c>random_seed</c> to remember the current seed.</p>
<p>If a process calls
- <seealso marker="#uniform/0"><c>uniform/0</c></seealso> or
- <seealso marker="#uniform/1"><c>uniform/1</c></seealso>
+ <seemfa marker="#uniform/0"><c>uniform/0</c></seemfa> or
+ <seemfa marker="#uniform/1"><c>uniform/1</c></seemfa>
without setting a seed first,
- <seealso marker="#seed/0"><c>seed/0</c></seealso>
+ <seemfa marker="#seed/0"><c>seed/0</c></seemfa>
is called automatically.</p>
<p>The implementation changed in Erlang/OTP R15. Upgrading to R15 breaks
diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml
index 4fd7a6b3d7..11041e63b2 100644
--- a/lib/stdlib/doc/src/re.xml
+++ b/lib/stdlib/doc/src/re.xml
@@ -40,7 +40,7 @@
<p>This module contains regular expression matching functions for
strings and binaries.</p>
- <p>The <seealso marker="#regexp_syntax">regular expression</seealso>
+ <p>The <seeerl marker="#regexp_syntax">regular expression</seeerl>
syntax and semantics resemble that of Perl.</p>
<p>The matching algorithms of the library are based on the
@@ -101,8 +101,8 @@
<p>Compiles a regular expression, with the syntax
described below, into an internal format to be used later as a
parameter to
- <seealso marker="#run/2"><c>run/2</c></seealso> and
- <seealso marker="#run/3"><c>run/3</c></seealso>.</p>
+ <seemfa marker="#run/2"><c>run/2</c></seemfa> and
+ <seemfa marker="#run/3"><c>run/3</c></seemfa>.</p>
<p>Compiling the regular expression before matching is useful if
the same expression is to be used in matching against multiple
subjects during the lifetime of the program. Compiling once and
@@ -281,8 +281,8 @@
the subject up to "A" and never realize that the (*COMMIT)
instruction is to have made the matching fail. This option is only
relevant if you use "start-of-pattern items", as discussed in
- section <seealso marker="#regexp_syntax_details">PCRE Regular Expression
- Details</seealso>.</p>
+ section <seeerl marker="#regexp_syntax_details">PCRE Regular Expression
+ Details</seeerl>.</p>
</item>
<tag><c>ucp</c></tag>
<item>
@@ -330,7 +330,7 @@
regardless of where the names are positioned in the regular
expression. The order of the names is the same as the order of
captured subexpressions if <c>{capture, all_names}</c> is specified as
- an option to <seealso marker="#run/3"><c>run/3</c></seealso>.
+ an option to <seemfa marker="#run/3"><c>run/3</c></seemfa>.
You can therefore create a name-to-value mapping from the result of
<c>run/3</c> like this:</p>
<code>
@@ -365,7 +365,7 @@
<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
- <seealso marker="#run/3"><c>run/3</c></seealso>, except that option<c>
+ <seemfa marker="#run/3"><c>run/3</c></seemfa>, except that option<c>
capture</c> 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
@@ -402,7 +402,7 @@ re:replace("abcd","c","[\\&amp;]",[{return,list}]).</code>
<code>
"ab[&amp;]d"</code>
<p>As with <c>run/3</c>, compilation errors raise the <c>badarg</c>
- exception. <seealso marker="#compile/2"><c>compile/2</c></seealso>
+ exception. <seemfa marker="#compile/2"><c>compile/2</c></seemfa>
can be used to get more information about the error.</p>
</desc>
</func>
@@ -420,8 +420,8 @@ re:replace("abcd","c","[\\&amp;]",[{return,list}]).</code>
<name name="run" arity="3" since=""/>
<fsummary>Match a subject against regular expression and capture
subpatterns.</fsummary>
- <type_desc variable="CompileOpt">See <seealso marker="#compile_options">
- <c>compile/2</c></seealso>.</type_desc>
+ <type_desc variable="CompileOpt">See <seeerl marker="#compile_options">
+ <c>compile/2</c></seeerl>.</type_desc>
<desc>
<p>Executes a regular expression matching, and returns
<c>match/{match, <anno>Captured</anno>}</c> or <c>nomatch</c>. The
@@ -638,8 +638,8 @@ a?b?</code>
before that (each recursive call is also a call, but not
conversely). Both limits can however be changed, either by
setting limits directly in the regular expression string (see
- section <seealso marker="#regexp_syntax_details">PCRE Regular
- Eexpression Details</seealso>) or by specifying options to
+ section <seeerl marker="#regexp_syntax_details">PCRE Regular
+ Eexpression Details</seeerl>) or by specifying options to
<c>run/3</c>.</p>
</item>
</taglist>
@@ -819,8 +819,8 @@ re:run("ABCabcdABC",".*(abcd).*",[]).</code>
as if a <c>list()</c> of all the names <em>in
alphabetical order</em> was specified. The list of all
names can also be retrieved with
- <seealso marker="#inspect/2">
- <c>inspect/2</c></seealso>.</p>
+ <seemfa marker="#inspect/2">
+ <c>inspect/2</c></seemfa>.</p>
</item>
<tag><c>first</c></tag>
<item>
@@ -902,10 +902,10 @@ re:run("ABCabcdABC",".*(?&lt;FOO&gt;abcd).*",[{capture,['FOO']}]).</code>
<p>Returns 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 <seealso marker="erts:erlang#iolist_to_binary/1">
- <c>erlang:iolist_to_binary/1</c></seealso> or
- <seealso marker="unicode#characters_to_binary/2">
- <c>unicode:characters_to_binary/2</c></seealso> before
+ with <seemfa marker="erts:erlang#iolist_to_binary/1">
+ <c>erlang:iolist_to_binary/1</c></seemfa> or
+ <seemfa marker="unicode#characters_to_binary/2">
+ <c>unicode:characters_to_binary/2</c></seemfa> before
matching). Notice that option <c>unicode</c> results in
<em>byte-oriented</em> indexes in a (possibly virtual)
<em>UTF-8 encoded</em> binary. A byte index tuple
@@ -926,8 +926,8 @@ re:run("ABCabcdABC",".*(?&lt;FOO&gt;abcd).*",[{capture,['FOO']}]).</code>
regardless of character encoding). In that case the
<c>list</c> capturing can result in the same types of
tuples that
- <seealso marker="unicode#characters_to_list/2">
- <c>unicode:characters_to_list/2</c></seealso> can return,
+ <seemfa marker="unicode#characters_to_list/2">
+ <c>unicode:characters_to_list/2</c></seemfa> can return,
namely three-tuples with 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
@@ -987,7 +987,7 @@ re:run("cacb","c(a|b)",[global,{capture,[1],list}]).</code>
</item>
</taglist>
<p>For a descriptions of options only affecting the compilation step,
- see <seealso marker="#compile/2"><c>compile/2</c></seealso>.</p>
+ see <seemfa marker="#compile/2"><c>compile/2</c></seemfa>.</p>
</desc>
</func>
@@ -1003,15 +1003,15 @@ re:run("cacb","c(a|b)",[global,{capture,[1],list}]).</code>
<func>
<name name="split" arity="3" since=""/>
<fsummary>Split a string by tokens specified as a regular expression</fsummary>
- <type_desc variable="CompileOpt">See <seealso marker="#compile_options">
- <c>compile/2</c></seealso>.</type_desc>
+ <type_desc variable="CompileOpt">See <seeerl marker="#compile_options">
+ <c>compile/2</c></seeerl>.</type_desc>
<desc>
<p>Splits the input into parts by finding tokens according to the
regular expression supplied. The splitting is basically done by
running a global regular expression match and dividing the initial
string wherever a match occurs. The matching part of the string is
removed from the output.</p>
- <p>As in <seealso marker="#run/3"><c>run/3</c></seealso>, an <c>mp()</c>
+ <p>As in <seemfa marker="#run/3"><c>run/3</c></seemfa>, an <c>mp()</c>
compiled with option <c>unicode</c> requires
<c><anno>Subject</anno></c> to be a Unicode <c>charlist()</c>. If
compilation is done implicitly and the <c>unicode</c> compilation
@@ -1187,46 +1187,46 @@ re:split("Erlang","[lg]",[{return,list},{parts,4}]).</code>
<p>The reference material is divided into the following sections:</p>
<list type="bulleted">
- <item><seealso marker="#sect1">Special Start-of-Pattern Items</seealso>
+ <item><seeerl marker="#sect1">Special Start-of-Pattern Items</seeerl>
</item>
- <item><seealso marker="#sect2">Characters and Metacharacters</seealso>
+ <item><seeerl marker="#sect2">Characters and Metacharacters</seeerl>
</item>
- <item><seealso marker="#sect3">Backslash</seealso></item>
- <item><seealso marker="#sect4">Circumflex and Dollar</seealso></item>
- <item><seealso marker="#sect5">Full Stop (Period, Dot) and \N</seealso>
+ <item><seeerl marker="#sect3">Backslash</seeerl></item>
+ <item><seeerl marker="#sect4">Circumflex and Dollar</seeerl></item>
+ <item><seeerl marker="#sect5">Full Stop (Period, Dot) and \N</seeerl>
</item>
- <item><seealso marker="#sect6">Matching a Single Data Unit</seealso>
+ <item><seeerl marker="#sect6">Matching a Single Data Unit</seeerl>
</item>
- <item><seealso marker="#sect7">Square Brackets and Character
- Classes</seealso></item>
- <item><seealso marker="#sect8">Posix Character Classes</seealso></item>
- <item><seealso marker="#sect9">Vertical Bar</seealso></item>
- <item><seealso marker="#sect10">Internal Option Setting</seealso></item>
- <item><seealso marker="#sect11">Subpatterns</seealso></item>
- <item><seealso marker="#sect12">Duplicate Subpattern Numbers</seealso>
+ <item><seeerl marker="#sect7">Square Brackets and Character
+ Classes</seeerl></item>
+ <item><seeerl marker="#sect8">Posix Character Classes</seeerl></item>
+ <item><seeerl marker="#sect9">Vertical Bar</seeerl></item>
+ <item><seeerl marker="#sect10">Internal Option Setting</seeerl></item>
+ <item><seeerl marker="#sect11">Subpatterns</seeerl></item>
+ <item><seeerl marker="#sect12">Duplicate Subpattern Numbers</seeerl>
</item>
- <item><seealso marker="#sect13">Named Subpatterns</seealso></item>
- <item><seealso marker="#sect14">Repetition</seealso></item>
- <item><seealso marker="#sect15">Atomic Grouping and Possessive
- Quantifiers</seealso></item>
- <item><seealso marker="#sect16">Back References</seealso></item>
- <item><seealso marker="#sect17">Assertions</seealso></item>
- <item><seealso marker="#sect18">Conditional Subpatterns</seealso></item>
- <item><seealso marker="#sect19">Comments</seealso></item>
- <item><seealso marker="#sect20">Recursive Patterns</seealso></item>
- <item><seealso marker="#sect21">Subpatterns as Subroutines</seealso>
+ <item><seeerl marker="#sect13">Named Subpatterns</seeerl></item>
+ <item><seeerl marker="#sect14">Repetition</seeerl></item>
+ <item><seeerl marker="#sect15">Atomic Grouping and Possessive
+ Quantifiers</seeerl></item>
+ <item><seeerl marker="#sect16">Back References</seeerl></item>
+ <item><seeerl marker="#sect17">Assertions</seeerl></item>
+ <item><seeerl marker="#sect18">Conditional Subpatterns</seeerl></item>
+ <item><seeerl marker="#sect19">Comments</seeerl></item>
+ <item><seeerl marker="#sect20">Recursive Patterns</seeerl></item>
+ <item><seeerl marker="#sect21">Subpatterns as Subroutines</seeerl>
</item>
- <item><seealso marker="#sect22">Oniguruma Subroutine Syntax</seealso>
+ <item><seeerl marker="#sect22">Oniguruma Subroutine Syntax</seeerl>
</item>
- <item><seealso marker="#sect23">Backtracking Control</seealso></item>
+ <item><seeerl marker="#sect23">Backtracking Control</seeerl></item>
</list>
</section>
<section>
<marker id="sect1"></marker>
<title>Special Start-of-Pattern Items</title>
- <p>Some options that can be passed to <seealso marker="#compile/2">
- <c>compile/2</c></seealso> can also be set by special items at the start
+ <p>Some options that can be passed to <seemfa marker="#compile/2">
+ <c>compile/2</c></seemfa> can also be set by special items at the start
of a pattern. These are not Perl-compatible, but are provided to make
these options accessible to pattern writers who are not able to change
the program that processes the pattern. Any number of these items can
@@ -1236,8 +1236,8 @@ re:split("Erlang","[lg]",[{return,list},{parts,4}]).</code>
<p><em>UTF Support</em></p>
<p>Unicode support is basically UTF-8 based. To use Unicode characters, you
- either call <seealso marker="#compile/2"><c>compile/2</c></seealso> or
- <seealso marker="#run/3"><c>run/3</c></seealso> with option
+ either call <seemfa marker="#compile/2"><c>compile/2</c></seemfa> or
+ <seemfa marker="#run/3"><c>run/3</c></seemfa> with option
<c>unicode</c>, or the pattern must start with one of these special
sequences:</p>
@@ -1250,7 +1250,7 @@ re:split("Erlang","[lg]",[{return,list},{parts,4}]).</code>
lists to UTF-8 is not performed by the <c>re</c> functions. Therefore,
using these sequences is not recommended.
Add option <c>unicode</c> when running
- <seealso marker="#compile/2"><c>compile/2</c></seealso> instead.</p>
+ <seemfa marker="#compile/2"><c>compile/2</c></seemfa> instead.</p>
<p>Some applications that allow their users to supply patterns can wish to
restrict them to non-UTF data for security reasons. If option
@@ -1297,7 +1297,7 @@ re:split("Erlang","[lg]",[{return,list},{parts,4}]).</code>
</taglist>
<p>These override the default and the options specified to
- <seealso marker="#compile/2"><c>compile/2</c></seealso>. For example, the
+ <seemfa marker="#compile/2"><c>compile/2</c></seemfa>. For example, the
following pattern changes the convention to CR:</p>
<code>
@@ -1312,13 +1312,13 @@ re:split("Erlang","[lg]",[{return,list},{parts,4}]).</code>
affect what the \R escape sequence matches. By default, this is any
Unicode newline sequence, for Perl compatibility. However, this can be
changed; see the description of \R in section
- <seealso marker="#newline_sequences">Newline Sequences</seealso>. A change
+ <seeerl marker="#newline_sequences">Newline Sequences</seeerl>. A change
of the \R setting can be combined with a change of the newline
convention.</p>
<p><em>Setting Match and Recursion Limits</em></p>
- <p>The caller of <seealso marker="#run/3"><c>run/3</c></seealso> can set a
+ <p>The caller of <seemfa marker="#run/3"><c>run/3</c></seemfa> can set a
limit on the number of times the internal match() function is called and
on the maximum depth of recursive calls. These facilities are provided to
catch runaway matches that are provoked by patterns with huge matching
@@ -2184,7 +2184,7 @@ foo\Kbar</code>
they are independent of multiline mode. These three assertions are not
affected by options <c>notbol</c> or <c>noteol</c>, which affect only the
behavior of the circumflex and dollar metacharacters. However, if argument
- <c>startoffset</c> of <seealso marker="#run/3"><c>run/3</c></seealso> is
+ <c>startoffset</c> of <seemfa marker="#run/3"><c>run/3</c></seemfa> is
non-zero, indicating that matching is to start at a point other than the
beginning of the subject, \A can never match. The difference between \Z
and \z is that \Z matches before a newline at the end of the string and
@@ -2218,7 +2218,7 @@ foo\Kbar</code>
<p>Outside a character class, in the default matching mode, the circumflex
character is an assertion that is true only if the current matching point
is at the start of the subject string. If argument <c>startoffset</c> of
- <seealso marker="#run/3"><c>run/3</c></seealso> is non-zero, circumflex
+ <seemfa marker="#run/3"><c>run/3</c></seemfa> is non-zero, circumflex
can never match if option <c>multiline</c> is unset. Inside a character
class, circumflex has an entirely different meaning (see below).</p>
@@ -2321,8 +2321,8 @@ foo\Kbar</code>
(?=[\x{10000}-\x{1fffff}])(\C)(\C)(\C)(\C))</code>
<p>A group that starts with (?| resets the capturing parentheses numbers in
- each alternative (see section <seealso marker="#sect12">Duplicate
- Subpattern Numbers</seealso>). The assertions at the start of each branch
+ each alternative (see section <seeerl marker="#sect12">Duplicate
+ Subpattern Numbers</seeerl>). The assertions at the start of each branch
check the next UTF-8 character for values whose encoding uses 1, 2, 3, or
4 bytes, respectively. The individual bytes of the character are then
captured by the appropriate number of groups.</p>
@@ -2416,8 +2416,8 @@ foo\Kbar</code>
digit. In UTF modes, option <c>ucp</c> affects the meanings of \d, \s, \w
and their uppercase partners, just as it does when they appear outside a
character class, as described in section
- <seealso marker="#generic_character_types">Generic Character
- Types</seealso> earlier. The escape sequence \b has a different meaning
+ <seeerl marker="#generic_character_types">Generic Character
+ Types</seeerl> earlier. The escape sequence \b has a different meaning
inside a character class; it matches the backspace character. The
sequences \B, \N, \R, and \X are not special inside a character class.
Like any other unrecognized escape sequences, they are treated as the
@@ -2572,7 +2572,7 @@ gilbert|sullivan</code>
permitted (matching the empty string). The matching process tries each
alternative in turn, from left to right, and the first that succeeds is
used. If the alternatives are within a subpattern (defined in section
- <seealso marker="#sect11">Subpatterns</seealso>), "succeeds" means
+ <seeerl marker="#sect11">Subpatterns</seeerl>), "succeeds" means
matching the remaining main pattern and the alternative in the
subpattern.</p>
</section>
@@ -2607,7 +2607,7 @@ gilbert|sullivan</code>
subpattern parentheses), the change applies to the remainder of the
pattern that follows.</p>
<p>An option change within a subpattern (see section
- <seealso marker="#sect11">Subpatterns</seealso>) affects only that part of
+ <seeerl marker="#sect11">Subpatterns</seeerl>) affects only that part of
the subpattern that follows it. So, the following matches abc and aBc and
no other strings (assuming <c>caseless</c> is not used):</p>
@@ -2632,8 +2632,8 @@ gilbert|sullivan</code>
compiling or matching functions are called. Sometimes the pattern can
contain special leading sequences, such as (*CRLF), to override what
the application has set or what has been defaulted. Details are provided
- in section <seealso marker="#newline_sequences">
- Newline Sequences</seealso> earlier.</p>
+ in section <seeerl marker="#newline_sequences">
+ Newline Sequences</seeerl> earlier.</p>
<p>The (*UTF8) and (*UCP) leading sequences can be used to set UTF and
Unicode property modes. They are equivalent to setting options
<c>unicode</c> and <c>ucp</c>, respectively. The (*UTF) sequence is a
@@ -2664,7 +2664,7 @@ cat(aract|erpillar|)</code>
<p>It sets up the subpattern as a capturing subpattern. That is, when
the complete pattern matches, that portion of the subject string that
matched the subpattern is passed back to the caller through the
- return value of <seealso marker="#run/3"><c>run/3</c></seealso>.</p>
+ return value of <seemfa marker="#run/3"><c>run/3</c></seemfa>.</p>
</item>
</taglist>
@@ -2776,8 +2776,8 @@ the ((?:red|white) (king|queen))</code>
<p>Names consist of up to 32 alphanumeric characters and underscores, but
must start with a non-digit. Named capturing parentheses are still allocated
numbers as well as names, exactly as if the names were not present.
- The <c>capture</c> specification to <seealso marker="#run/3">
- <c>run/3</c></seealso> can use named values if they are present in the
+ The <c>capture</c> specification to <seemfa marker="#run/3">
+ <c>run/3</c></seemfa> can use named values if they are present in the
regular expression.</p>
<p>By default, a name must be unique within a pattern, but this constraint
@@ -2803,7 +2803,7 @@ the ((?:red|white) (king|queen))</code>
<p>For capturing named subpatterns which names are not unique, the first
matching occurrence (counted from left to right in the subject) is
- returned from <seealso marker="#run/3"><c>run/3</c></seealso>, if the name
+ returned from <seemfa marker="#run/3"><c>run/3</c></seemfa>, if the name
is specified in the <c>values</c> part of the <c>capture</c> statement.
The <c>all_names</c> capturing value matches all the names in the same
way.</p>
@@ -2875,8 +2875,8 @@ z{2,4}</code>
<p>The quantifier {0} is permitted, causing the expression to behave as if
the previous item and the quantifier were not present. This can be useful
for subpatterns that are referenced as subroutines from elsewhere in the
- pattern (but see also section <seealso marker="#defining_subpatterns">
- Defining Subpatterns for Use by Reference Only</seealso>). Items other
+ pattern (but see also section <seeerl marker="#defining_subpatterns">
+ Defining Subpatterns for Use by Reference Only</seeerl>). Items other
than subpatterns that have a {0} quantifier are omitted from the compiled
pattern.</p>
@@ -3125,8 +3125,8 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</code>
subpattern whose number is 10 or more using this syntax, as a sequence
such as \50 is interpreted as a character defined in octal. For more
details of the handling of digits following a backslash, see section
- <seealso marker="#non_printing_characters">Non-Printing
- Characters</seealso> earlier. There is no such problem when named
+ <seeerl marker="#non_printing_characters">Non-Printing
+ Characters</seeerl> earlier. There is no such problem when named
parentheses are used. A back reference to any subpattern is possible
using named parentheses (see below).</p>
@@ -3156,8 +3156,8 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</code>
<p>A back reference matches whatever matched the capturing subpattern in the
current subject string, rather than anything matching the subpattern
- itself (section <seealso marker="#sect21">Subpattern as
- Subroutines</seealso> describes a way of doing that). So, the
+ itself (section <seeerl marker="#sect21">Subpattern as
+ Subroutines</seeerl> describes a way of doing that). So, the
following pattern matches "sense and sensibility" and "response and
responsibility", but not "sense and responsibility":</p>
@@ -3202,7 +3202,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</code>
number. If the pattern continues with a digit character, some delimiter
must be used to terminate the back reference. If option <c>extended</c> is
set, this can be whitespace. Otherwise an empty comment (see section
- <seealso marker="#sect19">Comments</seealso>) can be used.</p>
+ <seeerl marker="#sect19">Comments</seeerl>) can be used.</p>
<p><em>Recursive Back References</em></p>
@@ -3463,8 +3463,8 @@ abcd$</code>
<p>If the text between the parentheses consists of a sequence of digits,
the condition is true if a capturing subpattern of that number has
previously matched. If more than one capturing subpattern with the same
- number exists (see section <seealso marker="#sect12">
- Duplicate Subpattern Numbers</seealso> earlier), the condition is true if
+ number exists (see section <seeerl marker="#sect12">
+ Duplicate Subpattern Numbers</seeerl> earlier), the condition is true if
any of them have matched. An alternative notation is to precede the
digits with a plus or minus sign. In this case, the subpattern number is
relative rather than absolute. The most recently opened parentheses can be
@@ -3594,8 +3594,8 @@ abcd$</code>
character or character sequence in the pattern. Which characters are
interpreted as newlines is controlled by the options passed to a
compiling function or by a special sequence at the start of the pattern,
- as described in section <seealso marker="#newline_conventions">
- Newline Conventions</seealso> earlier.</p>
+ as described in section <seeerl marker="#newline_conventions">
+ Newline Conventions</seeerl> earlier.</p>
<p>Notice that the end of this type of comment is a literal newline sequence
in the pattern; escape sequences that happen to represent a newline do not
@@ -3929,8 +3929,8 @@ $re = qr{\( (?: (?&gt;[^()]+) | (?p{$re}) )* \)}x;</code>
running of a match, any included backtracking verbs are not processed.
processed. You can suppress the start-of-match optimizations by setting
option <c>no_start_optimize</c> when calling
- <seealso marker="#compile/2"><c>compile/2</c></seealso> or
- <seealso marker="#run/3"><c>run/3</c></seealso>, or by starting the
+ <seemfa marker="#compile/2"><c>compile/2</c></seemfa> or
+ <seemfa marker="#run/3"><c>run/3</c></seemfa>, or by starting the
pattern with (*NO_START_OPT).</p>
<p>Experiments with Perl suggest that it too has similar optimizations,
@@ -3979,7 +3979,7 @@ A((?:A|B(*ACCEPT)|C)D)</code>
<note>
<p>In Erlang, there is no interface to retrieve a mark with
- <seealso marker="#run/2"><c>run/2,3</c></seealso>, so only the secondary
+ <seemfa marker="#run/2"><c>run/2,3</c></seemfa>, so only the secondary
purpose is relevant to the Erlang programmer.</p>
<p>The rest of this section is therefore deliberately not adapted for
@@ -4061,7 +4061,7 @@ No match, mark = B</code>
(*COMMIT)</code>
<p>If (*COMMIT) is the only backtracking verb that is encountered, once it
- has been passed, <seealso marker="#run/2"><c>run/2,3</c></seealso> is
+ has been passed, <seemfa marker="#run/2"><c>run/2,3</c></seemfa> is
committed to find a match at the current starting point, or not at all,
for example:</p>
diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml
index 8d61833d1f..d12f537b07 100644
--- a/lib/stdlib/doc/src/ref_man.xml
+++ b/lib/stdlib/doc/src/ref_man.xml
@@ -84,6 +84,7 @@
<xi:include href="sets.xml"/>
<xi:include href="shell.xml"/>
<xi:include href="shell_default.xml"/>
+ <xi:include href="shell_docs.xml"/>
<xi:include href="slave.xml"/>
<xi:include href="sofs.xml"/>
<xi:include href="string.xml"/>
diff --git a/lib/stdlib/doc/src/sets.xml b/lib/stdlib/doc/src/sets.xml
index 07ce41b7a7..291425c35b 100644
--- a/lib/stdlib/doc/src/sets.xml
+++ b/lib/stdlib/doc/src/sets.xml
@@ -39,7 +39,7 @@
The representation of a set is undefined.</p>
<p>This module provides the same interface as the
- <seealso marker="ordsets"><c>ordsets(3)</c></seealso> module
+ <seeerl marker="ordsets"><c>ordsets(3)</c></seeerl> module
but with an undefined representation. One difference is
that while this module considers two elements as different if they
do not match (<c>=:=</c>), <c>ordsets</c> considers two elements as
@@ -50,7 +50,7 @@
<datatype>
<name name="set" n_vars="1"/>
<desc><p>As returned by
- <seealso marker="#new/0"><c>new/0</c></seealso>.</p></desc>
+ <seemfa marker="#new/0"><c>new/0</c></seemfa>.</p></desc>
</datatype>
<datatype>
<name name="set" n_vars="0"/>
@@ -220,8 +220,8 @@
<section>
<title>See Also</title>
- <p><seealso marker="gb_sets"><c>gb_sets(3)</c></seealso>,
- <seealso marker="ordsets"><c>ordsets(3)</c></seealso></p>
+ <p><seeerl marker="gb_sets"><c>gb_sets(3)</c></seeerl>,
+ <seeerl marker="ordsets"><c>ordsets(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml
index d0d796655c..62ce63ec33 100644
--- a/lib/stdlib/doc/src/shell.xml
+++ b/lib/stdlib/doc/src/shell.xml
@@ -44,8 +44,8 @@
values, which can then be incorporated in later commands.
How many commands and results to save can be determined by the user,
either interactively, by calling
- <seealso marker="#history/1"><c>history/1</c></seealso> and
- <seealso marker="#results/1"><c>results/1</c></seealso>,
+ <seemfa marker="#history/1"><c>history/1</c></seemfa> and
+ <seemfa marker="#results/1"><c>results/1</c></seemfa>,
or by setting the application configuration
parameters <c>shell_history_length</c> and
<c>shell_saved_results</c> for the STDLIB application.</p>
@@ -53,8 +53,8 @@
<p>The shell uses a helper process for evaluating commands
to protect the history mechanism from exceptions. By
default the evaluator process is killed when an exception
- occurs, but by calling <seealso marker="#catch_exception/1">
- <c>catch_exception/1</c></seealso> or by
+ occurs, but by calling <seemfa marker="#catch_exception/1">
+ <c>catch_exception/1</c></seemfa> or by
setting the application configuration parameter
<c>shell_catch_exception</c> for the STDLIB application
this behavior can be changed. See also the example below.</p>
@@ -119,6 +119,9 @@
<section>
<title>Shell Commands</title>
+ <p>The commands below are the built-in shell commands that are always
+ available. In most system the commands listed in the <seeerl marker="c">c(3)</seeerl>
+ module are also available in the shell.</p>
<taglist>
<tag><c>b()</c></tag>
<item>
@@ -232,7 +235,7 @@
<p>Reads record definitions from files. Existing
definitions of any of the record names read are replaced.
<c>Wildcard</c> is a wildcard string as defined in
- <seealso marker="filelib"><c>filelib(3)</c></seealso>,
+ <seeerl marker="filelib"><c>filelib(3)</c></seeerl>,
but not an atom.</p>
</item>
<tag><c>rr(WildcardOrModule, RecordNames)</c></tag>
@@ -280,8 +283,8 @@ Eshell V5.3 (abort with ^G)
{4,abcd}</pre>
<p>Command 3 builds the tuple <c>Descriptor</c>, evaluating the BIF
- <seealso marker="erts:erlang#list_to_atom/1"><c>list_to_atom/1</c>
- </seealso>.</p>
+ <seemfa marker="erts:erlang#list_to_atom/1"><c>list_to_atom/1</c>
+ </seemfa>.</p>
<pre>
4> <input>L.</input>
@@ -740,8 +743,8 @@ q - quit erlang
<p>If you want an Erlang node to have a remote job active from the start
(rather than the default local job), start Erlang with flag
- <c>-remsh</c>, for example,
- <c>erl -sname this_node -remsh other_node@other_host</c></p>
+ <seecom marker="erts:erl#remsh"><c>-remsh</c></seecom>, for example,
+ <c>erl -remsh other_node@other_host</c></p>
</section>
<section>
@@ -782,7 +785,7 @@ q - quit erlang
<p>These callback functions are called from local and
non-local evaluation function handlers, described in the
- <seealso marker="erl_eval"><c>erl_eval</c></seealso>
+ <seeerl marker="erl_eval"><c>erl_eval</c></seeerl>
manual page. (Arguments in <c>ArgList</c> are evaluated before the
callback functions are called.)</p>
@@ -805,8 +808,8 @@ q - quit erlang
</item>
<item>
<p>From a normal shell session, call function
- <seealso marker="#start_restricted/1">
- <c>start_restricted/1</c></seealso>. This exits the current evaluator
+ <seemfa marker="#start_restricted/1">
+ <c>start_restricted/1</c></seemfa>. This exits the current evaluator
and starts a new one in restricted mode.</p>
</item>
</list>
@@ -842,8 +845,8 @@ q - quit erlang
</item>
<item>
<p>If the restricted shell is activated using
- <seealso marker="#start_restricted/1">
- <c>start_restricted/1</c></seealso> and the callback module cannot
+ <seemfa marker="#start_restricted/1">
+ <c>start_restricted/1</c></seemfa> and the callback module cannot
be loaded, an error report is sent to the error logger and the call
returns <c>{error,Reason}</c>.</p>
</item>
@@ -855,8 +858,8 @@ q - quit erlang
<p>The default shell prompt function displays the name of the node
(if the node can be part of a distributed system) and the
current command number. The user can customize the prompt
- function by calling <seealso marker="#prompt_func/1">
- <c>prompt_func/1</c></seealso> or by setting application
+ function by calling <seemfa marker="#prompt_func/1">
+ <c>prompt_func/1</c></seemfa> or by setting application
configuration parameter <c>shell_prompt_func</c> for the
STDLIB application.</p>
diff --git a/lib/stdlib/doc/src/shell_default.xml b/lib/stdlib/doc/src/shell_default.xml
index 75bf89ba8d..18af8b7273 100644
--- a/lib/stdlib/doc/src/shell_default.xml
+++ b/lib/stdlib/doc/src/shell_default.xml
@@ -48,7 +48,7 @@
2> <input>c(foo).</input>
{ok, foo}</pre>
- <p>In command one, module <seealso marker="lists"><c>lists</c></seealso> is
+ <p>In command one, module <seeerl marker="lists"><c>lists</c></seeerl> is
called. In command two, no module name is specified. The shell searches
module <c>user_default</c> followed by module <c>shell_default</c> for
function <c>c/1</c>.</p>
diff --git a/lib/stdlib/doc/src/shell_docs.xml b/lib/stdlib/doc/src/shell_docs.xml
new file mode 100644
index 0000000000..bd3e3600f7
--- /dev/null
+++ b/lib/stdlib/doc/src/shell_docs.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2020</year><year>2020</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>shell_docs</title>
+ <prepared>Lukas Larsson</prepared>
+ <responsible></responsible>
+ <docno>1</docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2020-02-19</date>
+ <rev>A</rev>
+ <file>shell_docs.xml</file>
+ </header>
+ <module since="OTP 23.0">shell_docs</module>
+ <modulesummary>Functions used to render EEP-48 style documentation for a shell.</modulesummary>
+ <description>
+ <p>This module can be used to render function and type documentation
+ to be printed in a shell. It can only render EEP-48 documentation of the format
+ <c>application/erlang+html</c>. For more information about this format see
+ <seeguide marker="erl_docgen:doc_storage">Documentation Storage</seeguide>
+ in Erl_Docgen's User's Guide.
+ </p>
+ </description>
+
+ <datatypes>
+ <datatype>
+ <name name="docs_v1"/>
+ <desc>
+ <p>
+ The record holding EEP-48 documentation for a module.
+ You can use <seemfa marker="kernel:code#get_doc/1">code:get_doc/1</seemfa>
+ to fetch this information from a module.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="config"/>
+ <desc>
+ <p>
+ The configuration of how the documentation should be rendered.
+ </p>
+ <taglist>
+ <tag>encoding</tag>
+ <item>
+ Configure the encoding that should be used by
+ the renderer for graphical details such as bullet-points.
+ By default <c>shell_docs</c> uses the value returned
+ by <seemfa marker="io#getopts/0"><c>io:getopts()</c></seemfa>.</item>
+ <tag>ansi</tag>
+ <item>
+ Configure whether <url href="https://en.wikipedia.org/wiki/ANSI_escape_code">
+ ansi escape codes</url> should be used to
+ render graphical details such as bold and underscore. By default
+ <c>shell_docs</c> will try to determine if the receiving shell
+ supports ansi escape codes. It is possible to override
+ the automated check by setting the kernel configuration parameter
+ <c>shell_docs_ansi</c> to a <c>boolean()</c> value.</item>
+ <tag>columns</tag>
+ <item>
+ Configure how wide the target documentation should be rendered.
+ By default <c>shell_docs</c> used the value returned by
+ <seemfa marker="io#columns/0"><c>io:columns()</c></seemfa>.
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="chunk_element_block_type"/>
+ <name name="chunk_element_inline_type"/>
+ <name name="chunk_element_type"/>
+ <desc>
+ <p>
+ The HTML tags allowed in <c>application/erlang+html</c>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="chunk_element_attr"/>
+ <name name="chunk_element_attrs"/>
+ <name name="chunk_element"/>
+ <name name="chunk_elements"/>
+ <desc>
+ </desc>
+ </datatype>
+ </datatypes>
+
+ <funcs>
+
+ <func>
+ <name name="render" arity="2" since="OTP 23.0"/>
+ <name name="render" arity="3" clause_i="1" since="OTP 23.2"/>
+ <name name="render" arity="3" clause_i="2" since="OTP 23.0"/>
+ <name name="render" arity="4" clause_i="1" since="OTP 23.2"/>
+ <name name="render" arity="4" clause_i="2" since="OTP 23.0"/>
+ <name name="render" arity="5" since="OTP 23.2"/>
+ <fsummary>Render the documentation for a module or function.</fsummary>
+ <desc>
+ <p>Render the documentation for a module or function.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="render_type" arity="2" since="OTP 23.0"/>
+ <name name="render_type" arity="3" clause_i="1" since="OTP 23.2"/>
+ <name name="render_type" arity="3" clause_i="2" since="OTP 23.0"/>
+ <name name="render_type" arity="4" clause_i="1" since="OTP 23.2"/>
+ <name name="render_type" arity="4" clause_i="2" since="OTP 23.0"/>
+ <name name="render_type" arity="5" since="OTP 23.2"/>
+ <fsummary>Render the documentation of a type in a module.</fsummary>
+ <desc>
+ <p>Render the documentation of a type in a module.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="render_callback" arity="2" since="OTP 23.0"/>
+ <name name="render_callback" arity="3" clause_i="1" since="OTP 23.2"/>
+ <name name="render_callback" arity="3" clause_i="2" since="OTP 23.0"/>
+ <name name="render_callback" arity="4" clause_i="1" since="OTP 23.2"/>
+ <name name="render_callback" arity="4" clause_i="2" since="OTP 23.0"/>
+ <name name="render_callback" arity="5" since="OTP 23.2"/>
+ <fsummary>Render the documentation of a callback in a module.</fsummary>
+ <desc>
+ <p>Render the documentation of a callback in a module.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="validate" arity="1" since="OTP 23.0"/>
+ <fsummary>Validate the documentation</fsummary>
+ <desc>
+ <p>This function can be used to do a basic validation of
+ the doc content of <c>application/erlang+html</c> format.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="normalize" arity="1" since="OTP 23.0"/>
+ <fsummary>Normalize the documentation</fsummary>
+ <desc>
+ <p>This function can be used to do whitespace normalization
+ of <c>application/erlang+html</c> documentation.</p>
+ </desc>
+ </func>
+
+ </funcs>
+</erlref>
diff --git a/lib/stdlib/doc/src/slave.xml b/lib/stdlib/doc/src/slave.xml
index f60e7e1d2f..92bd676691 100644
--- a/lib/stdlib/doc/src/slave.xml
+++ b/lib/stdlib/doc/src/slave.xml
@@ -51,7 +51,7 @@
<p>An alternative to the <c>ssh</c> program can be specified on
the command line to
- <seealso marker="erts:erl"><c>erl(1)</c></seealso> as follows:</p>
+ <seecom marker="erts:erl"><c>erl(1)</c></seecom> as follows:</p>
<pre>
-rsh Program</pre>
@@ -140,7 +140,7 @@ rpc:call(N, slave, pseudo, [node(), [pxw_server]]).</code>
<p>Argument <c><anno>Args</anno></c> 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"><c>erl(1)</c></seealso>.</p>
+ <seecom marker="erts:erl"><c>erl(1)</c></seecom>.</p>
<p>As an example, suppose that you want to start a slave node at
host <c>H</c> with node name <c>Name@H</c> and
want the slave node to have the following properties:</p>
@@ -197,7 +197,7 @@ slave:start(H, Name, Arg).</code>
executing process. If that process terminates, the slave node
also terminates.</p>
<p>For a description of arguments and return values, see
- <seealso marker="#start/1"><c>start/1,2,3</c></seealso>.</p>
+ <seemfa marker="#start/1"><c>start/1,2,3</c></seemfa>.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/sofs.xml b/lib/stdlib/doc/src/sofs.xml
index c2c6675258..9b96c4d39a 100644
--- a/lib/stdlib/doc/src/sofs.xml
+++ b/lib/stdlib/doc/src/sofs.xml
@@ -273,7 +273,7 @@
<p>If S is an element (T,&nbsp;X) of Sets, then T is a
<marker id="valid_type"></marker><em>valid type</em> of X, T is the
type of S, and X is the external set of S.
- <seealso marker="#from_term/2"><c>from_term/2</c></seealso> creates a
+ <seemfa marker="#from_term/2"><c>from_term/2</c></seemfa> creates a
set from a type and an Erlang term turned into an external set.</p>
<p>The sets represented by Sets are the elements of the range of
function Set from Sets to Erlang terms and sets of Erlang terms:</p>
@@ -288,7 +288,7 @@
</list>
<p>When there is no risk of confusion, elements of Sets are identified
with the sets they represent. For example, if U is the result of
- calling <seealso marker="#union/2"><c>union/2</c></seealso> with S1
+ calling <seemfa marker="#union/2"><c>union/2</c></seemfa> with S1
and S2 as arguments, then U is said to be the union of S1 and S2.
A more precise formulation is that Set(U) is the union of Set(S1)
and Set(S2).</p>
@@ -300,8 +300,8 @@
product of two sets R and S, and recall that the relative
product of R and S is defined if R is a binary relation to Y and
S is a binary relation from Y. The function that implements the
- relative product, <seealso marker="#relative_product/2">
- <c>relative_product/2</c></seealso>, checks
+ relative product, <seemfa marker="#relative_product/2">
+ <c>relative_product/2</c></seemfa>, checks
that the arguments represent binary relations by matching [{A,B}]
against the type of the first argument (Arg1 say), and [{C,D}]
against the type of the second argument (Arg2 say). The fact
@@ -316,13 +316,13 @@
set.</p>
<p>A few functions of this module
- (<seealso marker="#drestriction/3"><c>drestriction/3</c></seealso>,
- <seealso marker="#family_projection/2"><c>family_projection/2</c></seealso>,
- <seealso marker="#partition/2"><c>partition/2</c></seealso>,
- <seealso marker="#partition_family/2"><c>partition_family/2</c></seealso>,
- <seealso marker="#projection/2"><c>projection/2</c></seealso>,
- <seealso marker="#restriction/3"><c>restriction/3</c></seealso>,
- <seealso marker="#substitution/2"><c>substitution/2</c></seealso>)
+ (<seemfa marker="#drestriction/3"><c>drestriction/3</c></seemfa>,
+ <seemfa marker="#family_projection/2"><c>family_projection/2</c></seemfa>,
+ <seemfa marker="#partition/2"><c>partition/2</c></seemfa>,
+ <seemfa marker="#partition_family/2"><c>partition_family/2</c></seemfa>,
+ <seemfa marker="#projection/2"><c>projection/2</c></seemfa>,
+ <seemfa marker="#restriction/3"><c>restriction/3</c></seemfa>,
+ <seemfa marker="#substitution/2"><c>substitution/2</c></seemfa>)
accept an Erlang
function as a means to modify each element of a given unordered
set. <marker id="set_fun"></marker>Such a function, called
@@ -377,12 +377,12 @@ fun(S) -> sofs:partition(1, S) end
the execution time is in the worst case proportional to the sum
of the sizes of the input arguments and the returned value. A
few functions execute in constant time:
- <seealso marker="#from_external/2"><c>from_external/2</c></seealso>,
- <seealso marker="#is_empty_set/1"><c>is_empty_set/1</c></seealso>,
- <seealso marker="#is_set/1"><c>is_set/1</c></seealso>,
- <seealso marker="#is_sofs_set/1"><c>is_sofs_set/1</c></seealso>,
- <seealso marker="#to_external/1"><c>to_external/1</c></seealso>
- <seealso marker="#type/1"><c>type/1</c></seealso>.</p>
+ <seemfa marker="#from_external/2"><c>from_external/2</c></seemfa>,
+ <seemfa marker="#is_empty_set/1"><c>is_empty_set/1</c></seemfa>,
+ <seemfa marker="#is_set/1"><c>is_set/1</c></seemfa>,
+ <seemfa marker="#is_sofs_set/1"><c>is_sofs_set/1</c></seemfa>,
+ <seemfa marker="#to_external/1"><c>to_external/1</c></seemfa>
+ <seemfa marker="#type/1"><c>type/1</c></seemfa>.</p>
<p>The functions of this module exit the process with a
<c>badarg</c>, <c>bad_function</c>, or <c>type_mismatch</c>
@@ -399,53 +399,53 @@ fun(S) -> sofs:partition(1, S) end
</datatype>
<datatype>
<name name="binary_relation"></name>
- <desc><p>A <seealso marker="#binary_relation">binary
- relation</seealso>.</p></desc>
+ <desc><p>A <seeerl marker="#binary_relation">binary
+ relation</seeerl>.</p></desc>
</datatype>
<datatype>
<name name="external_set"></name>
- <desc><p>An <seealso marker="#external_set">external
- set</seealso>.</p></desc>
+ <desc><p>An <seeerl marker="#external_set">external
+ set</seeerl>.</p></desc>
</datatype>
<datatype>
<name name="family"></name>
- <desc><p>A <seealso marker="#family">family</seealso> (of subsets).</p>
+ <desc><p>A <seeerl marker="#family">family</seeerl> (of subsets).</p>
</desc>
</datatype>
<datatype>
<name name="a_function"></name>
- <desc><p>A <seealso marker="#function">function</seealso>.</p></desc>
+ <desc><p>A <seeerl marker="#function">function</seeerl>.</p></desc>
</datatype>
<datatype>
<name name="ordset"></name>
- <desc><p>An <seealso marker="#sets_definition">ordered
- set</seealso>.</p></desc>
+ <desc><p>An <seeerl marker="#sets_definition">ordered
+ set</seeerl>.</p></desc>
</datatype>
<datatype>
<name name="relation"></name>
- <desc><p>An <seealso marker="#n_ary_relation">n-ary relation</seealso>.
+ <desc><p>An <seeerl marker="#n_ary_relation">n-ary relation</seeerl>.
</p></desc>
</datatype>
<datatype>
<name name="a_set"></name>
- <desc><p>An <seealso marker="#sets_definition">unordered
- set</seealso>.</p></desc>
+ <desc><p>An <seeerl marker="#sets_definition">unordered
+ set</seeerl>.</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>
+ <desc><p>An <seeerl marker="#sets_definition">unordered
+ set</seeerl> of unordered sets.</p></desc>
</datatype>
<datatype>
<name name="set_fun"></name>
- <desc><p>A <seealso marker="#set_fun">SetFun</seealso>.</p></desc>
+ <desc><p>A <seeerl marker="#set_fun">SetFun</seeerl>.</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>
+ <desc><p>A <seeerl marker="#type">type</seeerl>.</p></desc>
</datatype>
<datatype>
<!-- Parameterized opaque types are NYI: -->
@@ -460,10 +460,10 @@ fun(S) -> sofs:partition(1, S) end
<name name="a_function" arity="2" since=""/>
<fsummary>Create a function.</fsummary>
<desc>
- <p>Creates a <seealso marker="#function">function</seealso>.
+ <p>Creates a <seeerl marker="#function">function</seeerl>.
<c>a_function(F,&nbsp;T)</c> is equivalent to
<c>from_term(F,&nbsp;T)</c> if the result is a function. If
- no <seealso marker="#type">type</seealso> is explicitly
+ no <seeerl marker="#type">type</seeerl> is explicitly
specified, <c>[{atom,&nbsp;atom}]</c> is used as the
function type.</p>
</desc>
@@ -476,10 +476,10 @@ fun(S) -> sofs:partition(1, S) end
<p>Returns the binary relation containing the elements
(E,&nbsp;Set) such that Set belongs to <c><anno>SetOfSets</anno></c>
and E belongs to Set. If <c>SetOfSets</c> is
- a <seealso marker="#partition">partition</seealso> of a set X and
+ a <seeerl marker="#partition">partition</seeerl> of a set X and
R is the equivalence relation in X induced by <c>SetOfSets</c>,
then the returned relation is
- the <seealso marker="#canonical_map">canonical map</seealso> from
+ the <seeerl marker="#canonical_map">canonical map</seeerl> from
X onto the equivalence classes with respect to R.</p>
<pre>
1> <input>Ss = sofs:from_term([[a,b],[b,c]]),</input>
@@ -493,7 +493,7 @@ fun(S) -> sofs:partition(1, S) end
<name name="composite" arity="2" since=""/>
<fsummary>Return the composite of two functions.</fsummary>
<desc>
- <p>Returns the <seealso marker="#composite">composite</seealso> of
+ <p>Returns the <seeerl marker="#composite">composite</seeerl> of
the functions <c><anno>Function1</anno></c> and
<c><anno>Function2</anno></c>.</p>
<pre>
@@ -510,7 +510,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Create the function that maps each element of a
set onto another set.</fsummary>
<desc>
- <p>Creates the <seealso marker="#function">function</seealso>
+ <p>Creates the <seeerl marker="#function">function</seeerl>
that maps each element of set <c>Set</c> onto <c>AnySet</c>.</p>
<pre>
1> <input>S = sofs:set([a,b]),</input>
@@ -525,7 +525,7 @@ fun(S) -> sofs:partition(1, S) end
<name name="converse" arity="1" since=""/>
<fsummary>Return the converse of a binary relation.</fsummary>
<desc>
- <p>Returns the <seealso marker="#converse">converse</seealso>
+ <p>Returns the <seeerl marker="#converse">converse</seeerl>
of the binary relation <c><anno>BinRel1</anno></c>.</p>
<pre>
1> <input>R1 = sofs:relation([{1,a},{2,b},{3,a}]),</input>
@@ -539,7 +539,7 @@ fun(S) -> sofs:partition(1, S) end
<name name="difference" arity="2" since=""/>
<fsummary>Return the difference of two sets.</fsummary>
<desc>
- <p>Returns the <seealso marker="#difference">difference</seealso> of
+ <p>Returns the <seeerl marker="#difference">difference</seeerl> of
the sets <c><anno>Set1</anno></c> and <c><anno>Set2</anno></c>.</p>
</desc>
</func>
@@ -549,14 +549,14 @@ fun(S) -> sofs:partition(1, S) end
<name name="digraph_to_family" arity="2" since=""/>
<fsummary>Create a family from a directed graph.</fsummary>
<desc>
- <p>Creates a <seealso marker="#family">family</seealso> from
+ <p>Creates a <seeerl marker="#family">family</seeerl> from
the directed graph <c><anno>Graph</anno></c>. Each vertex a of
<c><anno>Graph</anno></c> is
represented by a pair (a,&nbsp;{b[1],&nbsp;...,&nbsp;b[n]}),
where the b[i]:s are the out-neighbors of a. If no type is
explicitly specified, [{atom,&nbsp;[atom]}] is used as type of
the family. It is assumed that <c><anno>Type</anno></c> is
- a <seealso marker="#valid_type">valid type</seealso> of the
+ a <seeerl marker="#valid_type">valid type</seeerl> of the
external set of the family.</p>
<p>If G is a directed graph, it holds that the vertices and
edges of G are the same as the vertices and edges of
@@ -568,7 +568,7 @@ fun(S) -> sofs:partition(1, S) end
<name name="domain" arity="1" since=""/>
<fsummary>Return the domain of a binary relation.</fsummary>
<desc>
- <p>Returns the <seealso marker="#domain">domain</seealso> of
+ <p>Returns the <seeerl marker="#domain">domain</seeerl> of
the binary relation <c><anno>BinRel</anno></c>.</p>
<pre>
1> <input>R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),</input>
@@ -584,7 +584,7 @@ fun(S) -> sofs:partition(1, S) end
<desc>
<p>Returns the difference between the binary relation
<c><anno>BinRel1</anno></c>
- and the <seealso marker="#restriction">restriction</seealso>
+ and the <seeerl marker="#restriction">restriction</seeerl>
of <c><anno>BinRel1</anno></c> to <c><anno>Set</anno></c>.</p>
<pre>
1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input>
@@ -621,8 +621,8 @@ fun(S) -> sofs:partition(1, S) end
<name name="empty_set" arity="0" since=""/>
<fsummary>Return the untyped empty set.</fsummary>
<desc>
- <p>Returns the <seealso marker="#sets_definition">untyped empty
- set</seealso>. <c>empty_set()</c> is equivalent to
+ <p>Returns the <seeerl marker="#sets_definition">untyped empty
+ set</seeerl>. <c>empty_set()</c> is equivalent to
<c>from_term([],&nbsp;['_'])</c>.</p>
</desc>
</func>
@@ -631,10 +631,10 @@ fun(S) -> sofs:partition(1, S) end
<name name="extension" arity="3" since=""/>
<fsummary>Extend the domain of a binary relation.</fsummary>
<desc>
- <p>Returns the <seealso marker="#extension">extension</seealso> of
+ <p>Returns the <seeerl marker="#extension">extension</seeerl> of
<c><anno>BinRel1</anno></c> such that for
each element E in <c><anno>Set</anno></c> that does not belong to the
- <seealso marker="#domain">domain</seealso> of
+ <seeerl marker="#domain">domain</seeerl> of
<c><anno>BinRel1</anno></c>, <c><anno>BinRel2</anno></c> contains the
pair (E,&nbsp;<c>AnySet</c>).</p>
<pre>
@@ -652,10 +652,10 @@ fun(S) -> sofs:partition(1, S) end
<name name="family" arity="2" since=""/>
<fsummary>Create a family of subsets.</fsummary>
<desc>
- <p>Creates a <seealso marker="#family">family of subsets</seealso>.
+ <p>Creates a <seeerl marker="#family">family of subsets</seeerl>.
<c>family(F,&nbsp;T)</c> is equivalent to
<c>from_term(F,&nbsp;T)</c> if the result is a family. If
- no <seealso marker="#type">type</seealso> is explicitly
+ no <seeerl marker="#type">type</seeerl> is explicitly
specified, <c>[{atom,&nbsp;[atom]}]</c> is used as the
family type.</p>
</desc>
@@ -666,7 +666,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return the difference of two families.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> and <c><anno>Family2</anno></c>
- are <seealso marker="#family">families</seealso>, then
+ are <seeerl marker="#family">families</seeerl>, then
<c><anno>Family3</anno></c> is the family
such that the index set is equal to the index set of
<c><anno>Family1</anno></c>, and <c><anno>Family3</anno></c>[i] is
@@ -687,13 +687,13 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return a family of domains.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> is
- a <seealso marker="#family">family</seealso>
+ a <seeerl marker="#family">family</seeerl>
and <c><anno>Family1</anno></c>[i] is a binary relation for every i
in the index set of <c><anno>Family1</anno></c>,
then <c><anno>Family2</anno></c> is the family with the same index
set as <c><anno>Family1</anno></c> such
that <c><anno>Family2</anno></c>[i] is
- the <seealso marker="#domain">domain</seealso> of
+ the <seeerl marker="#domain">domain</seeerl> of
<c><anno>Family1</anno>[i]</c>.</p>
<pre>
1> <input>FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),</input>
@@ -708,13 +708,13 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return a family of fields.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> is
- a <seealso marker="#family">family</seealso>
+ a <seeerl marker="#family">family</seeerl>
and <c><anno>Family1</anno></c>[i] is a binary relation for every i
in the index set of <c><anno>Family1</anno></c>,
then <c><anno>Family2</anno></c> is the family with the same index
set as <c><anno>Family1</anno></c> such
that <c><anno>Family2</anno></c>[i] is
- the <seealso marker="#field">field</seealso> of
+ the <seeerl marker="#field">field</seeerl> of
<c><anno>Family1</anno></c>[i].</p>
<pre>
1> <input>FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),</input>
@@ -733,13 +733,13 @@ fun(S) -> sofs:partition(1, S) end
of sets of sets.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> is
- a <seealso marker="#family">family</seealso>
+ a <seeerl marker="#family">family</seeerl>
and <c><anno>Family1</anno></c>[i] is a set of sets for every i in
the index set of <c><anno>Family1</anno></c>,
then <c><anno>Family2</anno></c> is the family with the same index
set as <c><anno>Family1</anno></c> such
that <c><anno>Family2</anno></c>[i] is
- the <seealso marker="#intersection_n">intersection</seealso>
+ the <seeerl marker="#intersection_n">intersection</seeerl>
of <c><anno>Family1</anno></c>[i].</p>
<p>If <c><anno>Family1</anno></c>[i] is an empty set for some i,
the process exits with a <c>badarg</c> message.</p>
@@ -756,7 +756,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return the intersection of two families.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> and <c><anno>Family2</anno></c>
- are <seealso marker="#family">families</seealso>,
+ are <seeerl marker="#family">families</seeerl>,
then <c><anno>Family3</anno></c> is the family such that the index
set is the intersection of <c><anno>Family1</anno></c>:s and
<c><anno>Family2</anno></c>:s index sets,
@@ -776,7 +776,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return a family of modified subsets.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> is
- a <seealso marker="#family">family</seealso>,
+ a <seeerl marker="#family">family</seeerl>,
then <c><anno>Family2</anno></c> is the family with the same index
set as <c><anno>Family1</anno></c> such
that <c><anno>Family2</anno></c>[i] is the result of
@@ -795,13 +795,13 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return a family of ranges.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> is
- a <seealso marker="#family">family</seealso>
+ a <seeerl marker="#family">family</seeerl>
and <c><anno>Family1</anno></c>[i] is a binary relation for every i
in the index set of <c><anno>Family1</anno></c>,
then <c><anno>Family2</anno></c> is the family with the same index
set as <c><anno>Family1</anno></c> such
that <c><anno>Family2</anno></c>[i] is
- the <seealso marker="#range">range</seealso> of
+ the <seeerl marker="#range">range</seeerl> of
<c><anno>Family1</anno></c>[i].</p>
<pre>
1> <input>FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),</input>
@@ -816,15 +816,15 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Select a subset of a family using a predicate.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> is
- a <seealso marker="#family">family</seealso>,
+ a <seeerl marker="#family">family</seeerl>,
then <c><anno>Family2</anno></c> is
- the <seealso marker="#restriction">restriction</seealso> of
+ the <seeerl marker="#restriction">restriction</seeerl> of
<c><anno>Family1</anno></c> to those elements i of the index set
for which <c><anno>Fun</anno></c> applied
to <c><anno>Family1</anno></c>[i] returns
<c>true</c>. If <c><anno>Fun</anno></c> is a
tuple <c>{external,&nbsp;Fun2}</c>, then <c>Fun2</c> is applied to
- the <seealso marker="#external_set">external set</seealso>
+ the <seeerl marker="#external_set">external set</seeerl>
of <c><anno>Family1</anno></c>[i], otherwise <c><anno>Fun</anno></c>
is applied to <c><anno>Family1</anno></c>[i].</p>
<pre>
@@ -842,17 +842,17 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Create a directed graph from a family.</fsummary>
<desc>
<p>Creates a directed graph from
- <seealso marker="#family">family</seealso> <c><anno>Family</anno></c>.
+ <seeerl marker="#family">family</seeerl> <c><anno>Family</anno></c>.
For each pair (a,&nbsp;{b[1],&nbsp;...,&nbsp;b[n]})
of <c><anno>Family</anno></c>, vertex
a and the edges (a,&nbsp;b[i]) for
1&nbsp;&lt;=&nbsp;i&nbsp;&lt;=&nbsp;n are added to a newly
created directed graph.</p>
- <p>If no graph type is specified, <seealso marker="digraph#new/0">
- <c>digraph:new/0</c></seealso> is used for
+ <p>If no graph type is specified, <seemfa marker="digraph#new/0">
+ <c>digraph:new/0</c></seemfa> is used for
creating the directed graph, otherwise argument
<c><anno>GraphType</anno></c> is passed on as second argument to
- <seealso marker="digraph#new/1"><c>digraph:new/1</c></seealso>.</p>
+ <seemfa marker="digraph#new/1"><c>digraph:new/1</c></seemfa>.</p>
<p>It F is a family, it holds that F is a subset of
<c>digraph_to_family(family_to_digraph(F),&nbsp;type(F))</c>.
Equality holds if <c>union_of_family(F)</c> is a subset of
@@ -867,7 +867,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Create a binary relation from a family.</fsummary>
<desc>
<p>If <c><anno>Family</anno></c> is
- a <seealso marker="#family">family</seealso>,
+ a <seeerl marker="#family">family</seeerl>,
then <c><anno>BinRel</anno></c> is the binary relation containing
all pairs (i,&nbsp;x) such that i belongs to the index set
of <c><anno>Family</anno></c> and x belongs
@@ -885,13 +885,13 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return the union of a family of sets of sets.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> is
- a <seealso marker="#family">family</seealso>
+ a <seeerl marker="#family">family</seeerl>
and <c><anno>Family1</anno></c>[i] is a set of sets for each i in
the index set of <c><anno>Family1</anno></c>,
then <c><anno>Family2</anno></c> is the family with the same index
set as <c><anno>Family1</anno></c> such
that <c><anno>Family2</anno></c>[i] is
- the <seealso marker="#union_n">union</seealso> of
+ the <seeerl marker="#union_n">union</seeerl> of
<c><anno>Family1</anno></c>[i].</p>
<pre>
1> <input>F1 = sofs:from_term([{a,[[1,2],[2,3]]},{b,[[]]}]),</input>
@@ -908,7 +908,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return the union of two families.</fsummary>
<desc>
<p>If <c><anno>Family1</anno></c> and <c><anno>Family2</anno></c>
- are <seealso marker="#family">families</seealso>,
+ are <seeerl marker="#family">families</seeerl>,
then <c><anno>Family3</anno></c> is the family such that the index
set is the union of <c><anno>Family1</anno></c>:s
and <c><anno>Family2</anno></c>:s index sets,
@@ -929,7 +929,7 @@ fun(S) -> sofs:partition(1, S) end
<name name="field" arity="1" since=""/>
<fsummary>Return the field of a binary relation.</fsummary>
<desc>
- <p>Returns the <seealso marker="#field">field</seealso> of the
+ <p>Returns the <seeerl marker="#field">field</seeerl> of the
binary relation <c><anno>BinRel</anno></c>.</p>
<pre>
1> <input>R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),</input>
@@ -945,12 +945,12 @@ fun(S) -> sofs:partition(1, S) end
<name name="from_external" arity="2" since=""/>
<fsummary>Create a set.</fsummary>
<desc>
- <p>Creates a set from the <seealso marker="#external_set">external
- set</seealso> <c><anno>ExternalSet</anno></c> and
- the <seealso marker="#type">type</seealso> <c><anno>Type</anno></c>.
+ <p>Creates a set from the <seeerl marker="#external_set">external
+ set</seeerl> <c><anno>ExternalSet</anno></c> and
+ the <seeerl marker="#type">type</seeerl> <c><anno>Type</anno></c>.
It is assumed that <c><anno>Type</anno></c> is
- a <seealso marker="#valid_type">valid
- type</seealso> of <c><anno>ExternalSet</anno></c>.</p>
+ a <seeerl marker="#valid_type">valid
+ type</seeerl> of <c><anno>ExternalSet</anno></c>.</p>
</desc>
</func>
@@ -958,8 +958,8 @@ fun(S) -> sofs:partition(1, S) end
<name name="from_sets" arity="1" clause_i="1" since=""/>
<fsummary>Create a set out of a list of sets.</fsummary>
<desc>
- <p>Returns the <seealso marker="#sets_definition">unordered
- set</seealso> containing the sets of list
+ <p>Returns the <seeerl marker="#sets_definition">unordered
+ set</seeerl> containing the sets of list
<c><anno>ListOfSets</anno></c>.</p>
<pre>
1> <input>S1 = sofs:relation([{a,1},{b,2}]),</input>
@@ -974,8 +974,8 @@ fun(S) -> sofs:partition(1, S) end
<name name="from_sets" arity="1" clause_i="2" since=""/>
<fsummary>Create an ordered set out of a tuple of sets.</fsummary>
<desc>
- <p>Returns the <seealso marker="#sets_definition">ordered
- set</seealso> containing the sets of the non-empty tuple
+ <p>Returns the <seeerl marker="#sets_definition">ordered
+ set</seeerl> containing the sets of the non-empty tuple
<c><anno>TupleOfSets</anno></c>.</p>
</desc>
</func>
@@ -986,12 +986,12 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Create a set.</fsummary>
<desc>
<p><marker id="from_term"></marker>Creates an element
- of <seealso marker="#sets_definition">Sets</seealso> by
+ of <seeerl marker="#sets_definition">Sets</seeerl> by
traversing term <c><anno>Term</anno></c>, sorting lists,
removing duplicates, and
- deriving or verifying a <seealso marker="#valid_type">valid
- type</seealso> for the so obtained external set. An
- explicitly specified <seealso marker="#type">type</seealso>
+ deriving or verifying a <seeerl marker="#valid_type">valid
+ type</seeerl> for the so obtained external set. An
+ explicitly specified <seeerl marker="#type">type</seeerl>
<c><anno>Type</anno></c>
can be used to limit the depth of the traversal; an atomic
type stops the traversal, as shown by the following example
@@ -1019,14 +1019,14 @@ fun(S) -> sofs:partition(1, S) end
<input>sofs:to_external(Ss).</input>
[{a,[1,2,3]},{b,[4,5,6]}]</pre>
<p>Other functions that create sets are
- <seealso marker="#from_external/2"><c>from_external/2</c></seealso>
- and <seealso marker="#from_sets/1"><c>from_sets/1</c></seealso>.
+ <seemfa marker="#from_external/2"><c>from_external/2</c></seemfa>
+ and <seemfa marker="#from_sets/1"><c>from_sets/1</c></seemfa>.
Special cases of <c>from_term/2</c> are
- <seealso marker="#a_function/1"><c>a_function/1,2</c></seealso>,
- <seealso marker="#empty_set/0"><c>empty_set/0</c></seealso>,
- <seealso marker="#family/1"><c>family/1,2</c></seealso>,
- <seealso marker="#relation/1"><c>relation/1,2</c></seealso>, and
- <seealso marker="#set/1"><c>set/1,2</c></seealso>.</p>
+ <seemfa marker="#a_function/1"><c>a_function/1,2</c></seemfa>,
+ <seemfa marker="#empty_set/0"><c>empty_set/0</c></seemfa>,
+ <seemfa marker="#family/1"><c>family/1,2</c></seemfa>,
+ <seemfa marker="#relation/1"><c>relation/1,2</c></seemfa>, and
+ <seemfa marker="#set/1"><c>set/1,2</c></seemfa>.</p>
</desc>
</func>
@@ -1034,7 +1034,7 @@ fun(S) -> sofs:partition(1, S) end
<name name="image" arity="2" since=""/>
<fsummary>Return the image of a set under a binary relation.</fsummary>
<desc>
- <p>Returns the <seealso marker="#image">image</seealso> of
+ <p>Returns the <seeerl marker="#image">image</seeerl> of
set <c><anno>Set1</anno></c> under the binary
relation <c><anno>BinRel</anno></c>.</p>
<pre>
@@ -1051,7 +1051,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return the intersection of a set of sets.</fsummary>
<desc>
<p>Returns
- the <seealso marker="#intersection_n">intersection</seealso> of
+ the <seeerl marker="#intersection_n">intersection</seeerl> of
the set of sets <c><anno>SetOfSets</anno></c>.</p>
<p>Intersecting an empty set of sets exits the process with a
<c>badarg</c> message.</p>
@@ -1063,7 +1063,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return the intersection of two sets.</fsummary>
<desc>
<p>Returns
- the <seealso marker="#intersection">intersection</seealso> of
+ the <seeerl marker="#intersection">intersection</seeerl> of
<c><anno>Set1</anno></c> and <c><anno>Set2</anno></c>.</p>
</desc>
</func>
@@ -1073,7 +1073,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return the intersection of a family.</fsummary>
<desc>
<p>Returns the intersection of
- <seealso marker="#family">family</seealso> <c><anno>Family</anno></c>.
+ <seeerl marker="#family">family</seeerl> <c><anno>Family</anno></c>.
</p>
<p>Intersecting an empty family exits the process with a
<c>badarg</c> message.</p>
@@ -1089,7 +1089,7 @@ fun(S) -> sofs:partition(1, S) end
<name name="inverse" arity="1" since=""/>
<fsummary>Return the inverse of a function.</fsummary>
<desc>
- <p>Returns the <seealso marker="#inverse">inverse</seealso>
+ <p>Returns the <seeerl marker="#inverse">inverse</seeerl>
of function <c><anno>Function1</anno></c>.</p>
<pre>
1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input>
@@ -1104,8 +1104,8 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Return the inverse image of a set under
a binary relation.</fsummary>
<desc>
- <p>Returns the <seealso marker="#inverse_image">inverse
- image</seealso> of <c><anno>Set1</anno></c> under the binary
+ <p>Returns the <seeerl marker="#inverse_image">inverse
+ image</seeerl> of <c><anno>Set1</anno></c> under the binary
relation <c><anno>BinRel</anno></c>.</p>
<pre>
1> <input>R = sofs:relation([{1,a},{2,b},{2,c},{3,d}]),</input>
@@ -1121,7 +1121,7 @@ fun(S) -> sofs:partition(1, S) end
<fsummary>Test for a function.</fsummary>
<desc>
<p>Returns <c>true</c> if the binary relation <c><anno>BinRel</anno></c>
- is a <seealso marker="#function">function</seealso> or the
+ is a <seeerl marker="#function">function</seeerl> or the
untyped empty set, otherwise <c>false</c>.</p>
</desc>
</func>
@@ -1132,7 +1132,7 @@ fun(S) -> sofs:partition(1, S) end
<desc>
<p>Returns <c>true</c> if <c><anno>Set1</anno></c>
and <c><anno>Set2</anno></c>
- are <seealso marker="#disjoint">disjoint</seealso>, otherwise
+ are <seeerl marker="#disjoint">disjoint</seeerl>, otherwise
<c>false</c>.</p>
</desc>
</func>
@@ -1152,7 +1152,7 @@ fun(S) -> sofs:partition(1, S) end
<desc>
<p>Returns <c>true</c> if <c><anno>AnySet1</anno></c>
and <c><anno>AnySet2</anno></c>
- are <seealso marker="#equal">equal</seealso>, otherwise
+ are <seeerl marker="#equal">equal</seeerl>, otherwise
<c>false</c>. The following example shows that <c>==/2</c> is
used when comparing sets for equality:</p>
<pre>
@@ -1168,7 +1168,7 @@ true</pre>
<fsummary>Test for an unordered set.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>AnySet</anno></c> is
- an <seealso marker="#sets_definition">unordered set</seealso>, and
+ an <seeerl marker="#sets_definition">unordered set</seeerl>, and
<c>false</c> if <c><anno>AnySet</anno></c> is an ordered set or an
atomic set.</p>
</desc>
@@ -1179,7 +1179,7 @@ true</pre>
<fsummary>Test for an unordered set.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Term</anno></c> is
- an <seealso marker="#sets_definition">unordered set</seealso>, an
+ an <seeerl marker="#sets_definition">unordered set</seeerl>, an
ordered set, or an atomic set, otherwise <c>false</c>.</p>
</desc>
</func>
@@ -1189,7 +1189,7 @@ true</pre>
<fsummary>Test two sets for subset.</fsummary>
<desc>
<p>Returns <c>true</c> if <c><anno>Set1</anno></c> is
- a <seealso marker="#subset">subset</seealso>
+ a <seeerl marker="#subset">subset</seeerl>
of <c><anno>Set2</anno></c>, otherwise <c>false</c>.</p>
</desc>
</func>
@@ -1199,7 +1199,7 @@ true</pre>
<fsummary>Test for a type.</fsummary>
<desc>
<p>Returns <c>true</c> if term <c><anno>Term</anno></c> is
- a <seealso marker="#type">type</seealso>.</p>
+ a <seeerl marker="#type">type</seeerl>.</p>
</desc>
</func>
@@ -1207,8 +1207,8 @@ true</pre>
<name name="join" arity="4" since=""/>
<fsummary>Return the join of two relations.</fsummary>
<desc>
- <p>Returns the <seealso marker="#natural_join">natural
- join</seealso> of the relations <c><anno>Relation1</anno></c>
+ <p>Returns the <seeerl marker="#natural_join">natural
+ join</seeerl> of the relations <c><anno>Relation1</anno></c>
and <c><anno>Relation2</anno></c> on coordinates <c><anno>I</anno></c>
and <c><anno>J</anno></c>.</p>
<pre>
@@ -1229,8 +1229,8 @@ true</pre>
{R[1],&nbsp;...,&nbsp;R[n]} of binary relations
and <c><anno>BinRel1</anno></c> is a binary relation,
then <c><anno>BinRel2</anno></c> is
- the <seealso marker="#multiple_relative_product">multiple relative
- product</seealso> of the ordered set
+ the <seeerl marker="#multiple_relative_product">multiple relative
+ product</seeerl> of the ordered set
(R[i],&nbsp;...,&nbsp;R[n]) and <c><anno>BinRel1</anno></c>.</p>
<pre>
1> <input>Ri = sofs:relation([{a,1},{b,2},{c,3}]),</input>
@@ -1254,7 +1254,7 @@ true</pre>
<name name="partition" arity="1" since=""/>
<fsummary>Return the coarsest partition given a set of sets.</fsummary>
<desc>
- <p>Returns the <seealso marker="#partition">partition</seealso> of
+ <p>Returns the <seeerl marker="#partition">partition</seeerl> of
the union of the set of sets <c><anno>SetOfSets</anno></c> such that
two elements are considered equal if they belong to the same
elements of <c><anno>SetOfSets</anno></c>.</p>
@@ -1271,7 +1271,7 @@ true</pre>
<name name="partition" arity="2" since=""/>
<fsummary>Return a partition of a set.</fsummary>
<desc>
- <p>Returns the <seealso marker="#partition">partition</seealso> of
+ <p>Returns the <seeerl marker="#partition">partition</seeerl> of
<c><anno>Set</anno></c> such that two elements are considered equal
if the results of applying <c><anno>SetFun</anno></c> are equal.</p>
<pre>
@@ -1288,7 +1288,7 @@ true</pre>
<fsummary>Return a partition of a set.</fsummary>
<desc>
<p>Returns a pair of sets that, regarded as constituting a
- set, forms a <seealso marker="#partition">partition</seealso> of
+ set, forms a <seeerl marker="#partition">partition</seeerl> of
<c><anno>Set1</anno></c>. If the
result of applying <c><anno>SetFun</anno></c> to an element of
<c><anno>Set1</anno></c> gives an element in <c><anno>Set2</anno></c>,
@@ -1310,14 +1310,14 @@ true</pre>
<name name="partition_family" arity="2" since=""/>
<fsummary>Return a family indexing a partition.</fsummary>
<desc>
- <p>Returns <seealso marker="#family">family</seealso>
+ <p>Returns <seeerl marker="#family">family</seeerl>
<c><anno>Family</anno></c> where the indexed set is
- a <seealso marker="#partition">partition</seealso>
+ a <seeerl marker="#partition">partition</seeerl>
of <c><anno>Set</anno></c> such that two elements are considered
equal if the results of applying <c><anno>SetFun</anno></c> are the
same value i. This i is the index that <c><anno>Family</anno></c>
- maps onto the <seealso marker="#equivalence_class">equivalence
- class</seealso>.</p>
+ maps onto the <seeerl marker="#equivalence_class">equivalence
+ class</seeerl>.</p>
<pre>
1> <input>S = sofs:relation([{a,a,a,a},{a,a,b,b},{a,b,b,b}]),</input>
<input>SetFun = {external, fun({A,_,C,_}) -> {A,C} end},</input>
@@ -1331,8 +1331,8 @@ true</pre>
<name name="product" arity="1" since=""/>
<fsummary>Return the Cartesian product of a tuple of sets.</fsummary>
<desc>
- <p>Returns the <seealso marker="#Cartesian_product_tuple">Cartesian
- product</seealso> of the non-empty tuple of sets
+ <p>Returns the <seeerl marker="#Cartesian_product_tuple">Cartesian
+ product</seeerl> of the non-empty tuple of sets
<c><anno>TupleOfSets</anno></c>. If (x[1],&nbsp;...,&nbsp;x[n]) is
an element of the n-ary relation <c><anno>Relation</anno></c>, then
x[i] is drawn from element i of <c><anno>TupleOfSets</anno></c>.</p>
@@ -1350,8 +1350,8 @@ true</pre>
<name name="product" arity="2" since=""/>
<fsummary>Return the Cartesian product of two sets.</fsummary>
<desc>
- <p>Returns the <seealso marker="#Cartesian_product">Cartesian
- product</seealso> of <c><anno>Set1</anno></c>
+ <p>Returns the <seeerl marker="#Cartesian_product">Cartesian
+ product</seeerl> of <c><anno>Set1</anno></c>
and <c><anno>Set2</anno></c>.</p>
<pre>
1> <input>S1 = sofs:set([1,2]),</input>
@@ -1373,7 +1373,7 @@ true</pre>
applying <c><anno>SetFun</anno></c> to the element.</p>
<p>If <c><anno>SetFun</anno></c> is a number i&nbsp;&gt;=&nbsp;1 and
<c><anno>Set1</anno></c> is a relation, then the returned set is
- the <seealso marker="#projection">projection</seealso> of
+ the <seeerl marker="#projection">projection</seeerl> of
<c><anno>Set1</anno></c> onto coordinate i.</p>
<pre>
1> <input>S1 = sofs:from_term([{1,a},{2,b},{3,a}]),</input>
@@ -1387,7 +1387,7 @@ true</pre>
<name name="range" arity="1" since=""/>
<fsummary>Return the range of a binary relation.</fsummary>
<desc>
- <p>Returns the <seealso marker="#range">range</seealso> of the
+ <p>Returns the <seeerl marker="#range">range</seeerl> of the
binary relation <c><anno>BinRel</anno></c>.</p>
<pre>
1> <input>R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),</input>
@@ -1402,10 +1402,10 @@ true</pre>
<name name="relation" arity="2" since=""/>
<fsummary>Create a relation.</fsummary>
<desc>
- <p>Creates a <seealso marker="#relation">relation</seealso>.
+ <p>Creates a <seeerl marker="#relation">relation</seeerl>.
<c>relation(R,&nbsp;T)</c> is equivalent to
<c>from_term(R,&nbsp;T)</c>, if T is
- a <seealso marker="#type">type</seealso> and the result is a
+ a <seeerl marker="#type">type</seeerl> and the result is a
relation. If <c><anno>Type</anno></c> is an integer N, then
<c>[{atom,&nbsp;...,&nbsp;atom}])</c>, where the tuple size
is N, is used as type of the relation. If no type is
@@ -1420,11 +1420,11 @@ true</pre>
<name name="relation_to_family" arity="1" since=""/>
<fsummary>Create a family from a binary relation.</fsummary>
<desc>
- <p>Returns <seealso marker="#family">family</seealso>
+ <p>Returns <seeerl marker="#family">family</seeerl>
<c><anno>Family</anno></c> such that the index set is equal to
- the <seealso marker="#domain">domain</seealso> of the binary
+ the <seeerl marker="#domain">domain</seeerl> of the binary
relation <c><anno>BinRel</anno></c>, and <c><anno>Family</anno></c>[i]
- is the <seealso marker="#image">image</seealso> of the set of i
+ is the <seeerl marker="#image">image</seeerl> of the set of i
under <c><anno>BinRel</anno></c>.</p>
<pre>
1> <input>R = sofs:relation([{b,1},{c,2},{c,3}]),</input>
@@ -1444,13 +1444,13 @@ true</pre>
[R[1],&nbsp;...,&nbsp;R[n]] of binary relations and
<c><anno>BinRel1</anno></c>
is a binary relation, then <c><anno>BinRel2</anno></c> is the
- <seealso marker="#tuple_relative_product">relative product</seealso>
+ <seeerl marker="#tuple_relative_product">relative product</seeerl>
of the ordered set (R[i],&nbsp;...,&nbsp;R[n]) and
<c><anno>BinRel1</anno></c>.</p>
<p>If <c><anno>BinRel1</anno></c> 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],
+ the <seeerl marker="#Cartesian_product_tuple">Cartesian
+ product</seeerl> of the ranges of R[i],
range&nbsp;R[1]&nbsp;&times;&nbsp;...&nbsp;&times;&nbsp;range&nbsp;R[n],
is used instead (intuitively, nothing is "lost").</p>
<pre>
@@ -1470,8 +1470,8 @@ true</pre>
<fsummary>Return the relative product of
two binary relations.</fsummary>
<desc>
- <p>Returns the <seealso marker="#relative_product">relative
- product</seealso> of the binary relations <c><anno>BinRel1</anno></c>
+ <p>Returns the <seeerl marker="#relative_product">relative
+ product</seeerl> of the binary relations <c><anno>BinRel1</anno></c>
and <c><anno>BinRel2</anno></c>.</p>
</desc>
</func>
@@ -1481,9 +1481,9 @@ true</pre>
<fsummary>Return the relative_product of
two binary relations.</fsummary>
<desc>
- <p>Returns the <seealso marker="#relative_product">relative
- product</seealso> of
- the <seealso marker="#converse">converse</seealso> of the
+ <p>Returns the <seeerl marker="#relative_product">relative
+ product</seeerl> of
+ the <seeerl marker="#converse">converse</seeerl> of the
binary relation <c><anno>BinRel1</anno></c> and the binary
relation <c><anno>BinRel2</anno></c>.</p>
<pre>
@@ -1501,7 +1501,7 @@ true</pre>
<name name="restriction" arity="2" since=""/>
<fsummary>Return a restriction of a binary relation.</fsummary>
<desc>
- <p>Returns the <seealso marker="#restriction">restriction</seealso> of
+ <p>Returns the <seeerl marker="#restriction">restriction</seeerl> of
the binary relation <c><anno>BinRel1</anno></c>
to <c><anno>Set</anno></c>.</p>
<pre>
@@ -1534,10 +1534,10 @@ true</pre>
<name name="set" arity="2" since=""/>
<fsummary>Create a set of atoms or any type of sets.</fsummary>
<desc>
- <p>Creates an <seealso marker="#sets_definition">unordered
- set</seealso>. <c>set(L,&nbsp;T)</c> is equivalent to
+ <p>Creates an <seeerl marker="#sets_definition">unordered
+ set</seeerl>. <c>set(L,&nbsp;T)</c> is equivalent to
<c>from_term(L,&nbsp;T)</c>, if the result is an unordered
- set. If no <seealso marker="#type">type</seealso> is
+ set. If no <seeerl marker="#type">type</seeerl> is
explicitly specified, <c>[atom]</c> is used as the set type.</p>
</desc>
</func>
@@ -1550,7 +1550,7 @@ true</pre>
of <c><anno>Set1</anno></c> for which <c><anno>Fun</anno></c>
returns <c>true</c>. If <c><anno>Fun</anno></c> is a tuple
<c>{external,&nbsp;Fun2}</c>, <c>Fun2</c> is applied to the
- <seealso marker="#external_set">external set</seealso> of
+ <seeerl marker="#external_set">external set</seeerl> of
each element, otherwise <c><anno>Fun</anno></c> is applied to each
element.</p>
<pre>
@@ -1568,8 +1568,8 @@ true</pre>
<fsummary>Return the strict relation corresponding to
a given relation.</fsummary>
<desc>
- <p>Returns the <seealso marker="#strict_relation">strict
- relation</seealso> corresponding to the binary
+ <p>Returns the <seeerl marker="#strict_relation">strict
+ relation</seeerl> corresponding to the binary
relation <c><anno>BinRel1</anno></c>.</p>
<pre>
1> <input>R1 = sofs:relation([{1,1},{1,2},{2,1},{2,2}]),</input>
@@ -1604,7 +1604,7 @@ true</pre>
[{a,a},{b,b},{c,c}]</pre>
<p>Let <c>SetOfSets</c> be a set of sets and <c>BinRel</c> a binary
relation. The function that maps each element <c>Set</c> of
- <c>SetOfSets</c> onto the <seealso marker="#image">image</seealso>
+ <c>SetOfSets</c> onto the <seeerl marker="#image">image</seeerl>
of <c>Set</c> under <c>BinRel</c> is returned by the following
function:</p>
<pre>
@@ -1614,7 +1614,7 @@ images(SetOfSets, BinRel) ->
<p>External unordered sets are represented as sorted lists. So,
creating the image of a set under a relation R can traverse all
elements of R (to that comes the sorting of results, the
- image). In <seealso marker="#image/2"><c>image/2</c></seealso>,
+ image). In <seemfa marker="#image/2"><c>image/2</c></seemfa>,
<c>BinRel</c> is traversed once
for each element of <c>SetOfSets</c>, which can take too long. The
following efficient function can be used instead under the
@@ -1632,8 +1632,8 @@ images2(SetOfSets, BinRel) ->
<name name="symdiff" arity="2" since=""/>
<fsummary>Return the symmetric difference of two sets.</fsummary>
<desc>
- <p>Returns the <seealso marker="#symmetric_difference">symmetric
- difference</seealso> (or the Boolean sum)
+ <p>Returns the <seeerl marker="#symmetric_difference">symmetric
+ difference</seeerl> (or the Boolean sum)
of <c><anno>Set1</anno></c> and <c><anno>Set2</anno></c>.</p>
<pre>
1> <input>S1 = sofs:set([1,2,3]),</input>
@@ -1669,8 +1669,8 @@ images2(SetOfSets, BinRel) ->
<name name="to_external" arity="1" since=""/>
<fsummary>Return the elements of a set.</fsummary>
<desc>
- <p>Returns the <seealso marker="#external_set">external
- set</seealso> of an atomic, ordered, or unordered set.</p>
+ <p>Returns the <seeerl marker="#external_set">external
+ set</seeerl> of an atomic, ordered, or unordered set.</p>
</desc>
</func>
@@ -1689,7 +1689,7 @@ images2(SetOfSets, BinRel) ->
<name name="type" arity="1" since=""/>
<fsummary>Return the type of a set.</fsummary>
<desc>
- <p>Returns the <seealso marker="#type">type</seealso> of an
+ <p>Returns the <seeerl marker="#type">type</seeerl> of an
atomic, ordered, or unordered set.</p>
</desc>
</func>
@@ -1698,7 +1698,7 @@ images2(SetOfSets, BinRel) ->
<name name="union" arity="1" since=""/>
<fsummary>Return the union of a set of sets.</fsummary>
<desc>
- <p>Returns the <seealso marker="#union_n">union</seealso> of the
+ <p>Returns the <seeerl marker="#union_n">union</seeerl> of the
set of sets <c><anno>SetOfSets</anno></c>.</p>
</desc>
</func>
@@ -1707,7 +1707,7 @@ images2(SetOfSets, BinRel) ->
<name name="union" arity="2" since=""/>
<fsummary>Return the union of two sets.</fsummary>
<desc>
- <p>Returns the <seealso marker="#union">union</seealso> of
+ <p>Returns the <seeerl marker="#union">union</seeerl> of
<c><anno>Set1</anno></c> and <c><anno>Set2</anno></c>.</p>
</desc>
</func>
@@ -1716,7 +1716,7 @@ images2(SetOfSets, BinRel) ->
<name name="union_of_family" arity="1" since=""/>
<fsummary>Return the union of a family.</fsummary>
<desc>
- <p>Returns the union of <seealso marker="#family">family</seealso>
+ <p>Returns the union of <seeerl marker="#family">family</seeerl>
<c><anno>Family</anno></c>.</p>
<pre>
1> <input>F = sofs:family([{a,[0,2,4]},{b,[0,1,2]},{c,[2,3]}]),</input>
@@ -1731,10 +1731,10 @@ images2(SetOfSets, BinRel) ->
<fsummary>Return the weak relation corresponding to
a given relation.</fsummary>
<desc>
- <p>Returns a subset S of the <seealso marker="#weak_relation">weak
- relation</seealso> W
+ <p>Returns a subset S of the <seeerl marker="#weak_relation">weak
+ relation</seeerl> W
corresponding to the binary relation <c><anno>BinRel1</anno></c>.
- Let F be the <seealso marker="#field">field</seealso> of
+ Let F be the <seeerl marker="#field">field</seeerl> of
<c><anno>BinRel1</anno></c>. 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>
@@ -1749,11 +1749,11 @@ images2(SetOfSets, BinRel) ->
<section>
<title>See Also</title>
- <p><seealso marker="dict"><c>dict(3)</c></seealso>,
- <seealso marker="digraph"><c>digraph(3)</c></seealso>,
- <seealso marker="orddict"><c>orddict(3)</c></seealso>,
- <seealso marker="ordsets"><c>ordsets(3)</c></seealso>,
- <seealso marker="sets"><c>sets(3)</c></seealso></p>
+ <p><seeerl marker="dict"><c>dict(3)</c></seeerl>,
+ <seeerl marker="digraph"><c>digraph(3)</c></seeerl>,
+ <seeerl marker="orddict"><c>orddict(3)</c></seeerl>,
+ <seeerl marker="ordsets"><c>ordsets(3)</c></seeerl>,
+ <seeerl marker="sets"><c>sets(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml
index fd2d625685..9b11a6941c 100644
--- a/lib/stdlib/doc/src/specs.xml
+++ b/lib/stdlib/doc/src/specs.xml
@@ -51,6 +51,7 @@
<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_shell_docs.xml"/>
<xi:include href="../specs/specs_slave.xml"/>
<xi:include href="../specs/specs_sofs.xml"/>
<xi:include href="../specs/specs_string.xml"/>
diff --git a/lib/stdlib/doc/src/stdlib_app.xml b/lib/stdlib/doc/src/stdlib_app.xml
index f857cc394b..51caf4d14d 100644
--- a/lib/stdlib/doc/src/stdlib_app.xml
+++ b/lib/stdlib/doc/src/stdlib_app.xml
@@ -40,7 +40,7 @@
<title>Configuration</title>
<p>The following configuration parameters are defined for the STDLIB
application. For more information about configuration parameters, see the
- <seealso marker="kernel:app"><c>app(4)</c></seealso> module in Kernel.</p>
+ <seefile marker="kernel:app"><c>app(4)</c></seefile> module in Kernel.</p>
<taglist>
<tag><c>shell_esc = icl | abort</c></tag>
@@ -86,9 +86,9 @@
<section>
<title>See Also</title>
- <p><seealso marker="kernel:app"><c>app(4)</c></seealso>,
- <seealso marker="kernel:application"><c>application(3)</c></seealso>,
- <seealso marker="shell">shell(3)</seealso></p>
+ <p><seefile marker="kernel:app"><c>app(4)</c></seefile>,
+ <seeerl marker="kernel:application"><c>application(3)</c></seeerl>,
+ <seeerl marker="shell">shell(3)</seeerl></p>
</section>
</appref>
diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml
index d102191a57..4735f72943 100644
--- a/lib/stdlib/doc/src/string.xml
+++ b/lib/stdlib/doc/src/string.xml
@@ -36,8 +36,8 @@
<modulesummary>String processing functions.</modulesummary>
<description>
<p>This module provides functions for string processing.</p>
- <p>A string in this module is represented by <seealso marker="unicode#type-chardata">
- <c>unicode:chardata()</c></seealso>, that is, a list of codepoints,
+ <p>A string in this module is represented by <seetype marker="unicode#chardata">
+ <c>unicode:chardata()</c></seetype>, that is, a list of codepoints,
binaries with UTF-8-encoded codepoints
(<em>UTF-8 binaries</em>), or a mix of the two.</p>
<code>
@@ -66,11 +66,11 @@
Grapheme clusters for codepoints of class <c>prepend</c>
and non-modern (or decomposed) Hangul is not handled for performance
reasons in
- <seealso marker="#find/3"><c>find/3</c></seealso>,
- <seealso marker="#replace/3"><c>replace/3</c></seealso>,
- <seealso marker="#split/2"><c>split/2</c></seealso>,
- <seealso marker="#lexemes/2"><c>split/2</c></seealso> and
- <seealso marker="#trim/3"><c>trim/3</c></seealso>.
+ <seemfa marker="#find/3"><c>find/3</c></seemfa>,
+ <seemfa marker="#replace/3"><c>replace/3</c></seemfa>,
+ <seemfa marker="#split/2"><c>split/2</c></seemfa>,
+ <seemfa marker="#lexemes/2"><c>split/2</c></seemfa> and
+ <seemfa marker="#trim/3"><c>trim/3</c></seemfa>.
</p>
<p>
Splitting and appending strings is to be done on grapheme clusters
@@ -80,8 +80,8 @@
</p>
<p>
Most of the functions expect all input to be normalized to one form,
- see for example <seealso marker="unicode#characters_to_nfc_list/1">
- <c>unicode:characters_to_nfc_list/1</c></seealso>.
+ see for example <seemfa marker="unicode#characters_to_nfc_list/1">
+ <c>unicode:characters_to_nfc_list/1</c></seemfa>.
</p>
<p>
Language or locale specific handling of input is not considered
@@ -107,10 +107,10 @@
4> string:lexemes(&lt;&lt;"foo bar">>, " ").
[&lt;&lt;"foo">>,&lt;&lt;"bar">>]</code>
<p>This module has been reworked in Erlang/OTP 20 to
- handle <seealso marker="unicode#type-chardata">
- <c>unicode:chardata()</c></seealso> and operate on grapheme
- clusters. The <seealso marker="#oldapi"> <c>old
- functions</c></seealso> that only work on Latin-1 lists as input
+ handle <seetype marker="unicode#chardata">
+ <c>unicode:chardata()</c></seetype> and operate on grapheme
+ clusters. The <seeerl marker="#oldapi"> <c>old
+ functions</c></seeerl> that only work on Latin-1 lists as input
are still available but should not be used, they will be
deprecated in a future release.
</p>
@@ -137,7 +137,7 @@
Converts <c><anno>String</anno></c> to a case-agnostic
comparable string. Function <c>casefold/1</c> is preferred
over <c>lowercase/1</c> when two strings are to be compared
- for equality. See also <seealso marker="#equal/4"><c>equal/4</c></seealso>.
+ for equality. See also <seemfa marker="#equal/4"><c>equal/4</c></seemfa>.
</p>
<p><em>Example:</em></p>
<pre>
@@ -175,16 +175,16 @@
</p>
<p>
If <c><anno>IgnoreCase</anno></c> is <c>true</c>
- the function does <seealso marker="#casefold/1">
- <c>casefold</c>ing</seealso> on the fly before the equality test.
+ the function does <seemfa marker="#casefold/1">
+ <c>casefold</c>ing</seemfa> on the fly before the equality test.
</p>
<p>If <c><anno>Norm</anno></c> is not <c>none</c>
the function applies normalization on the fly before the equality test.
There are four available normalization forms:
- <seealso marker="unicode#characters_to_nfc_list/1"> <c>nfc</c></seealso>,
- <seealso marker="unicode#characters_to_nfd_list/1"> <c>nfd</c></seealso>,
- <seealso marker="unicode#characters_to_nfkc_list/1"> <c>nfkc</c></seealso>, and
- <seealso marker="unicode#characters_to_nfkd_list/1"> <c>nfkd</c></seealso>.
+ <seemfa marker="unicode#characters_to_nfc_list/1"> <c>nfc</c></seemfa>,
+ <seemfa marker="unicode#characters_to_nfd_list/1"> <c>nfd</c></seemfa>,
+ <seemfa marker="unicode#characters_to_nfkc_list/1"> <c>nfkc</c></seemfa>, and
+ <seemfa marker="unicode#characters_to_nfkd_list/1"> <c>nfkd</c></seemfa>.
</p>
<p>By default,
<c><anno>IgnoreCase</anno></c> is <c>false</c> and
@@ -273,7 +273,7 @@ true</pre>
adjacent separator graphemes clusters in <c><anno>String</anno></c>
are treated as one. That is, there are no empty
strings in the resulting list of lexemes.
- See also <seealso marker="#split/3"><c>split/3</c></seealso> which returns
+ See also <seemfa marker="#split/3"><c>split/3</c></seemfa> which returns
empty strings.
</p>
<p>Notice that <c>[$\r,$\n]</c> is one grapheme cluster.</p>
@@ -294,7 +294,7 @@ true</pre>
Converts <c><anno>String</anno></c> to lowercase.
</p>
<p>
- Notice that function <seealso marker="#casefold/1"><c>casefold/1</c></seealso>
+ Notice that function <seemfa marker="#casefold/1"><c>casefold/1</c></seemfa>
should be used when converting a string to
be tested for equality.
</p>
@@ -622,7 +622,7 @@ ÖÄÅ</pre>
<p>
Converts <c><anno>String</anno></c> to uppercase.
</p>
- <p>See also <seealso marker="#titlecase/1"><c>titlecase/1</c></seealso>.</p>
+ <p>See also <seemfa marker="#titlecase/1"><c>titlecase/1</c></seemfa>.</p>
<p><em>Example:</em></p>
<pre>
1> <input>string:uppercase("Michał").</input>
@@ -632,22 +632,23 @@ ÖÄÅ</pre>
</funcs>
- <section>
- <marker id="oldapi"/>
- <title>Obsolete API functions</title>
- <p>Here follows the function of the old API.
- These functions only work on a list of Latin-1 characters.
- </p>
- <note><p>
- The functions are kept for backward compatibility, but are
- not recommended.
- They will be deprecated in a future release.
- </p>
- <p>Any undocumented functions in <c>string</c> are not to be used.</p>
- </note>
- </section>
+
<funcs>
+ <fsdescription>
+ <marker id="oldapi"/>
+ <title>Obsolete API functions</title>
+ <p>Here follows the function of the old API.
+ These functions only work on a list of Latin-1 characters.
+ </p>
+ <note><p>
+ The functions are kept for backward compatibility, but are
+ not recommended.
+ They will be deprecated in a future release.
+ </p>
+ <p>Any undocumented functions in <c>string</c> are not to be used.</p>
+ </note>
+ </fsdescription>
<func>
<name name="centre" arity="2" since=""/>
<name name="centre" arity="3" since=""/>
@@ -656,9 +657,9 @@ ÖÄÅ</pre>
<p>Returns a string, where <c><anno>String</anno></c> is centered in the
string and surrounded by blanks or <c><anno>Character</anno></c>.
The resulting string has length <c><anno>Number</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#pad/3"><c>pad/3</c></seealso>.
+ <seemfa marker="#pad/3"><c>pad/3</c></seemfa>.
</p>
</desc>
</func>
@@ -671,9 +672,9 @@ ÖÄÅ</pre>
<p>Returns a string consisting of <c><anno>Number</anno></c> characters
<c><anno>Character</anno></c>. Optionally, the string can end with
string <c><anno>Tail</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="lists#duplicate/2"><c>lists:duplicate/2</c></seealso>.</p>
+ <seemfa marker="lists#duplicate/2"><c>lists:duplicate/2</c></seemfa>.</p>
</desc>
</func>
@@ -685,9 +686,9 @@ ÖÄÅ</pre>
<p>Returns the index of the first occurrence of
<c><anno>Character</anno></c> in <c><anno>String</anno></c>. Returns
<c>0</c> if <c><anno>Character</anno></c> does not occur.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#find/2"><c>find/2</c></seealso>.</p>
+ <seemfa marker="#find/2"><c>find/2</c></seemfa>.</p>
</desc>
</func>
@@ -699,13 +700,13 @@ ÖÄÅ</pre>
<c><anno>String2</anno></c> to form a new string
<c><anno>String3</anno></c>, which is returned.</p>
<p>
- This function is <seealso marker="#oldapi">obsolete</seealso>.
+ This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use <c>[<anno>String1</anno>, <anno>String2</anno>]</c> as
<c>Data</c> argument, and call
- <seealso marker="unicode#characters_to_list/2">
- <c>unicode:characters_to_list/2</c></seealso> or
- <seealso marker="unicode#characters_to_binary/2">
- <c>unicode:characters_to_binary/2</c></seealso>
+ <seemfa marker="unicode#characters_to_list/2">
+ <c>unicode:characters_to_list/2</c></seemfa> or
+ <seemfa marker="unicode#characters_to_binary/2">
+ <c>unicode:characters_to_binary/2</c></seemfa>
to flatten the output.
</p>
</desc>
@@ -717,9 +718,9 @@ ÖÄÅ</pre>
<desc>
<p>Returns a string containing <c><anno>String</anno></c> repeated
<c><anno>Number</anno></c> times.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="lists#duplicate/2"><c>lists:duplicate/2</c></seealso>.</p>
+ <seemfa marker="lists#duplicate/2"><c>lists:duplicate/2</c></seemfa>.</p>
</desc>
</func>
@@ -730,9 +731,9 @@ ÖÄÅ</pre>
<p>Returns the length of the maximum initial segment of
<c><anno>String</anno></c>, which consists entirely of characters
not from <c><anno>Chars</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#take/3"><c>take/3</c></seealso>.</p>
+ <seemfa marker="#take/3"><c>take/3</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> string:cspan("\t abcdef", " \t").
@@ -746,9 +747,9 @@ ÖÄÅ</pre>
<desc>
<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>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="lists#join/2"><c>lists:join/2</c></seealso>.</p>
+ <seemfa marker="lists#join/2"><c>lists:join/2</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> join(["one", "two", "three"], ", ").
@@ -766,10 +767,10 @@ ÖÄÅ</pre>
fixed. If <c>length(<anno>String</anno>)</c> &lt;
<c><anno>Number</anno></c>, then <c><anno>String</anno></c> is padded
with blanks or <c><anno>Character</anno></c>s.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#pad/2"><c>pad/2</c></seealso> or
- <seealso marker="#pad/3"><c>pad/3</c></seealso>.</p>
+ <seemfa marker="#pad/2"><c>pad/2</c></seemfa> or
+ <seemfa marker="#pad/3"><c>pad/3</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> string:left("Hello",10,$.).
@@ -782,9 +783,9 @@ ÖÄÅ</pre>
<fsummary>Return the length of a string.</fsummary>
<desc>
<p>Returns the number of characters in <c><anno>String</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#length/1"><c>length/1</c></seealso>.</p>
+ <seemfa marker="#length/1"><c>length/1</c></seemfa>.</p>
</desc>
</func>
@@ -796,9 +797,9 @@ ÖÄÅ</pre>
<p>Returns the index of the last occurrence of
<c><anno>Character</anno></c> in <c><anno>String</anno></c>. Returns
<c>0</c> if <c><anno>Character</anno></c> does not occur.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#find/3"><c>find/3</c></seealso>.</p>
+ <seemfa marker="#find/3"><c>find/3</c></seemfa>.</p>
</desc>
</func>
@@ -812,9 +813,9 @@ ÖÄÅ</pre>
fixed. If the length of <c>(<anno>String</anno>)</c> &lt;
<c><anno>Number</anno></c>, then <c><anno>String</anno></c> is padded
with blanks or <c><anno>Character</anno></c>s.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#pad/3"><c>pad/3</c></seealso>.</p>
+ <seemfa marker="#pad/3"><c>pad/3</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> string:right("Hello", 10, $.).
@@ -830,9 +831,9 @@ ÖÄÅ</pre>
<c><anno>SubString</anno></c> begins in <c><anno>String</anno></c>.
Returns <c>0</c> if <c><anno>SubString</anno></c>
does not exist in <c><anno>String</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#find/3"><c>find/3</c></seealso>.</p>
+ <seemfa marker="#find/3"><c>find/3</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> string:rstr(" Hello Hello World World ", "Hello World").
@@ -847,9 +848,9 @@ ÖÄÅ</pre>
<p>Returns the length of the maximum initial segment of
<c><anno>String</anno></c>, which consists entirely of characters
from <c><anno>Chars</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#take/2"><c>take/2</c></seealso>.</p>
+ <seemfa marker="#take/2"><c>take/2</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> string:span("\t abcdef", " \t").
@@ -865,9 +866,9 @@ ÖÄÅ</pre>
<c><anno>SubString</anno></c> begins in <c><anno>String</anno></c>.
Returns <c>0</c> if <c><anno>SubString</anno></c>
does not exist in <c><anno>String</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#find/2"><c>find/2</c></seealso>.</p>
+ <seemfa marker="#find/2"><c>find/2</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> string:str(" Hello Hello World World ", "Hello World").
@@ -887,9 +888,9 @@ ÖÄÅ</pre>
or <c>both</c>, indicates from which direction blanks are to be
removed. <c>strip/1</c> is equivalent to
<c>strip(String, both)</c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#trim/3"><c>trim/3</c></seealso>.</p>
+ <seemfa marker="#trim/3"><c>trim/3</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> string:strip("...Hello.....", both, $.).
@@ -905,9 +906,9 @@ ÖÄÅ</pre>
<p>Returns a substring of <c><anno>String</anno></c>, starting at
position <c><anno>Start</anno></c> to the end of the string, or to
and including position <c><anno>Stop</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#slice/3"><c>slice/3</c></seealso>.</p>
+ <seemfa marker="#slice/3"><c>slice/3</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
sub_string("Hello World", 4, 8).
@@ -923,9 +924,9 @@ sub_string("Hello World", 4, 8).
<p>Returns a substring of <c><anno>String</anno></c>, starting at
position <c><anno>Start</anno></c>, and ending at the end of the
string or at length <c><anno>Length</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#slice/3"><c>slice/3</c></seealso>.</p>
+ <seemfa marker="#slice/3"><c>slice/3</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> substr("Hello World", 4, 5).
@@ -941,9 +942,9 @@ sub_string("Hello World", 4, 8).
<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>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#nth_lexeme/3"><c>nth_lexeme/3</c></seealso>.</p>
+ <seemfa marker="#nth_lexeme/3"><c>nth_lexeme/3</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> string:sub_word(" Hello old boy !",3,$o).
@@ -965,11 +966,11 @@ sub_string("Hello World", 4, 8).
<p>The specified string or character is case-converted. Notice that
the supported character set is ISO/IEC 8859-1 (also called Latin 1);
all values outside this set are unchanged</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso> use
- <seealso marker="#lowercase/1"><c>lowercase/1</c></seealso>,
- <seealso marker="#uppercase/1"><c>uppercase/1</c></seealso>,
- <seealso marker="#titlecase/1"><c>titlecase/1</c></seealso> or
- <seealso marker="#casefold/1"><c>casefold/1</c></seealso>.</p>
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl> use
+ <seemfa marker="#lowercase/1"><c>lowercase/1</c></seemfa>,
+ <seemfa marker="#uppercase/1"><c>uppercase/1</c></seemfa>,
+ <seemfa marker="#titlecase/1"><c>titlecase/1</c></seemfa> or
+ <seemfa marker="#casefold/1"><c>casefold/1</c></seemfa>.</p>
</desc>
</func>
@@ -987,9 +988,9 @@ sub_string("Hello World", 4, 8).
adjacent separator characters in <c><anno>String</anno></c>
are treated as one. That is, there are no empty
strings in the resulting list of tokens.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#lexemes/2"><c>lexemes/2</c></seealso>.</p>
+ <seemfa marker="#lexemes/2"><c>lexemes/2</c></seemfa>.</p>
</desc>
</func>
@@ -1000,9 +1001,9 @@ sub_string("Hello World", 4, 8).
<desc>
<p>Returns the number of words in <c><anno>String</anno></c>, separated
by blanks or <c><anno>Character</anno></c>.</p>
- <p>This function is <seealso marker="#oldapi">obsolete</seealso>.
+ <p>This function is <seeerl marker="#oldapi">obsolete</seeerl>.
Use
- <seealso marker="#lexemes/2"><c>lexemes/2</c></seealso>.</p>
+ <seemfa marker="#lexemes/2"><c>lexemes/2</c></seemfa>.</p>
<p><em>Example:</em></p>
<code type="none">
> words(" Hello old boy!", $o).
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index f15b1a2dd3..6a44219084 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>2018</year>
+ <year>1996</year><year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -35,16 +35,16 @@
supervises other processes called child processes. A child
process can either be another supervisor or a worker process.
Worker processes are normally implemented using one of the
- <seealso marker="gen_event"><c>gen_event</c></seealso>,
- <seealso marker="gen_server"><c>gen_server</c></seealso>, or
- <seealso marker="gen_statem"><c>gen_statem</c></seealso>
+ <seeerl marker="gen_event"><c>gen_event</c></seeerl>,
+ <seeerl marker="gen_server"><c>gen_server</c></seeerl>, or
+ <seeerl marker="gen_statem"><c>gen_statem</c></seeerl>
behaviors. A supervisor implemented using this module has
a standard set of interface functions and include functionality
for tracing and error reporting. Supervisors are used to build a
hierarchical process structure called a supervision tree, a
nice way to structure a fault-tolerant application. For more
- information, see <seealso marker="doc/design_principles:sup_princ">
- Supervisor Behaviour</seealso> in OTP Design Principles.</p>
+ information, see <seeguide marker="system/design_principles:sup_princ">
+ Supervisor Behaviour</seeguide> in OTP Design Principles.</p>
<p>A supervisor expects the definition of which child processes to
supervise to be specified in a callback module exporting a
@@ -105,13 +105,13 @@ sup_flags() = #{strategy => strategy(), % optional
instances of the same process type, that is, running the same
code.</p>
<p>Functions
- <seealso marker="#delete_child/2"><c>delete_child/2</c></seealso> and
- <seealso marker="#restart_child/2"><c>restart_child/2</c></seealso>
+ <seemfa marker="#delete_child/2"><c>delete_child/2</c></seemfa> and
+ <seemfa marker="#restart_child/2"><c>restart_child/2</c></seemfa>
are invalid for <c>simple_one_for_one</c> supervisors and return
<c>{error,simple_one_for_one}</c> if the specified supervisor
uses this restart strategy.</p>
- <p>Function <seealso marker="#terminate_child/2">
- <c>terminate_child/2</c></seealso> can be used for
+ <p>Function <seemfa marker="#terminate_child/2">
+ <c>terminate_child/2</c></seemfa> can be used for
children under <c>simple_one_for_one</c> supervisors by
specifying the child's <c>pid()</c> as the second argument. If
instead the child specification identifier is used,
@@ -149,7 +149,7 @@ child_spec() = #{id => child_id(), % mandatory
modules => modules()} % optional</pre>
<p>The old tuple format is kept for backwards compatibility,
- see <seealso marker="#type-child_spec">child_spec()</seealso>,
+ see <seetype marker="#child_spec">child_spec()</seetype>,
but the map is preferred.</p>
<list type="bulleted">
@@ -251,8 +251,8 @@ child_spec() = #{id => child_id(), % mandatory
process is an event manager (<c>gen_event</c>) with a
dynamic set of callback modules, value <c>dynamic</c>
must be used. For more information about release handling, see
- <seealso marker="doc/design_principles:release_handling">
- Release Handling</seealso>
+ <seeguide marker="system/design_principles:release_handling">
+ Release Handling</seeguide>
in OTP Design Principles.</p>
<p>The <c>modules</c> key is optional. If it is not specified, it
defaults to <c>[M]</c>, where <c>M</c> comes from the
@@ -278,7 +278,7 @@ child_spec() = #{id => child_id(), % mandatory
<name name="child_spec"/>
<desc><p>The tuple format is kept for backward compatibility
only. A map is preferred; see more details
- <seealso marker="#child_spec">above</seealso>.</p></desc>
+ <seeerl marker="#child_spec">above</seeerl>.</p></desc>
</datatype>
<datatype>
<name name="mfargs"/>
@@ -300,13 +300,25 @@ child_spec() = #{id => child_id(), % mandatory
<name name="shutdown"/>
</datatype>
<datatype>
+ <name name="startchild_err"/>
+ </datatype>
+ <datatype>
+ <name name="startchild_ret"/>
+ </datatype>
+ <datatype>
+ <name name="startlink_err"/>
+ </datatype>
+ <datatype>
+ <name name="startlink_ret"/>
+ </datatype>
+ <datatype>
<name name="strategy"/>
</datatype>
<datatype>
<name name="sup_flags"/>
<desc><p>The tuple format is kept for backward compatibility
only. A map is preferred; see more details
- <seealso marker="#sup_flags">above</seealso>.</p></desc>
+ <seeerl marker="#sup_flags">above</seeerl>.</p></desc>
</datatype>
<datatype>
<name name="sup_ref"/>
@@ -333,8 +345,8 @@ child_spec() = #{id => child_id(), % mandatory
<fsummary>Return counts for the number of child specifications,
active children, supervisors, and workers.</fsummary>
<desc>
- <p>Returns a property list (see <seealso marker="proplists">
- <c>proplists</c></seealso>) containing the
+ <p>Returns a property list (see <seeerl marker="proplists">
+ <c>proplists</c></seeerl>) containing the
counts for each of the following elements of the supervisor's
child specifications and managed processes:</p>
<list type="bulleted">
@@ -361,7 +373,7 @@ child_spec() = #{id => child_id(), % mandatory
</item>
</list>
<p>For a description of <c><anno>SupRef</anno></c>, see
- <seealso marker="#SupRef"><c>start_child/2</c></seealso>.</p>
+ <seeerl marker="#SupRef"><c>start_child/2</c></seeerl>.</p>
</desc>
</func>
@@ -372,10 +384,10 @@ child_spec() = #{id => child_id(), % mandatory
<p>Tells 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
- <seealso marker="#terminate_child/2">
- <c>terminate_child/2</c></seealso> to terminate it.</p>
+ <seemfa marker="#terminate_child/2">
+ <c>terminate_child/2</c></seemfa> to terminate it.</p>
<p>For a description of <c><anno>SupRef</anno></c>, see
- <seealso marker="#SupRef"><c>start_child/2</c></seealso>.</p>
+ <seeerl marker="#SupRef"><c>start_child/2</c></seeerl>.</p>
<p>If successful, the function returns <c>ok</c>. If the child
specification identified by <c><anno>Id</anno></c> exists but the
corresponding child process is running or is about to be restarted,
@@ -395,7 +407,7 @@ child_spec() = #{id => child_id(), % mandatory
by <c>Id</c> under supervisor <c>SupRef</c>. The returned
map contains all keys, both mandatory and optional.</p>
<p>For a description of <c><anno>SupRef</anno></c>, see
- <seealso marker="#SupRef"><c>start_child/2</c></seealso>.</p>
+ <seeerl marker="#SupRef"><c>start_child/2</c></seeerl>.</p>
</desc>
</func>
@@ -413,7 +425,7 @@ child_spec() = #{id => child_id(), % mandatory
is automatically deleted when the child terminates; thus,
it is not possible to restart such children.</p>
<p>For a description of <c><anno>SupRef</anno></c>, see
- <seealso marker="#SupRef"><c>start_child/2</c></seealso>.</p>
+ <seeerl marker="#SupRef"><c>start_child/2</c></seeerl>.</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
@@ -528,8 +540,8 @@ child_spec() = #{id => child_id(), % mandatory
<item>
<p>If <c><anno>SupName</anno>={global,Name}</c>, the supervisor is
registered globally as <c>Name</c> using
- <seealso marker="kernel:global#register_name/2">
- <c>global:register_name/2</c></seealso>.</p>
+ <seemfa marker="kernel:global#register_name/2">
+ <c>global:register_name/2</c></seemfa>.</p>
</item>
<item>
<p>If
@@ -539,7 +551,7 @@ child_spec() = #{id => child_id(), % mandatory
export the functions <c>register_name/2</c>,
<c>unregister_name/1</c>, and <c>send/2</c>, which must behave
like the corresponding functions in
- <seealso marker="kernel:global"><c>global</c></seealso>. Thus,
+ <seeerl marker="kernel:global"><c>global</c></seeerl>. Thus,
<c>{via,global,<anno>Name</anno>}</c> is a valid reference.</p>
</item>
</list>
@@ -596,9 +608,9 @@ child_spec() = #{id => child_id(), % mandatory
kept by the supervisor. The child process can later be
restarted by the supervisor. The child process can also be
restarted explicitly by calling
- <seealso marker="#restart_child/2"><c>restart_child/2</c></seealso>.
+ <seemfa marker="#restart_child/2"><c>restart_child/2</c></seemfa>.
Use
- <seealso marker="#delete_child/2"><c>delete_child/2</c></seealso>
+ <seemfa marker="#delete_child/2"><c>delete_child/2</c></seemfa>
to remove the child specification.</p>
<p>If the child is temporary, the child specification is deleted as
soon as the process terminates. This means
@@ -616,7 +628,7 @@ child_spec() = #{id => child_id(), % mandatory
no child specification with the specified <c><anno>Id</anno></c>, the
function returns <c>{error,not_found}</c>.</p>
<p>For a description of <c><anno>SupRef</anno></c>, see
- <seealso marker="#SupRef"><c>start_child/2</c></seealso>.</p>
+ <seeerl marker="#SupRef"><c>start_child/2</c></seeerl>.</p>
</desc>
</func>
@@ -629,10 +641,10 @@ child_spec() = #{id => child_id(), % mandatory
specifications and child processes belonging to
supervisor <c><anno>SupRef</anno></c>.</p>
<p>Notice that calling this function when supervising many
- childrens under low memory conditions can cause an
+ children under low memory conditions can cause an
out of memory exception.</p>
<p>For a description of <c><anno>SupRef</anno></c>, see
- <seealso marker="#SupRef"><c>start_child/2</c></seealso>.</p>
+ <seeerl marker="#SupRef"><c>start_child/2</c></seeerl>.</p>
<p>The following information is given for each child
specification/process:</p>
<list type="bulleted">
@@ -658,13 +670,14 @@ child_spec() = #{id => child_id(), % mandatory
</func>
</funcs>
- <section>
- <title>Callback Functions</title>
- <p>The following function must be exported from a
- <c>supervisor</c> callback module.</p>
- </section>
+
<funcs>
+ <fsdescription>
+ <title>Callback Functions</title>
+ <p>The following function must be exported from a
+ <c>supervisor</c> callback module.</p>
+ </fsdescription>
<func>
<name since="">Module:init(Args) -> Result</name>
<fsummary>Return a supervisor specification.</fsummary>
@@ -672,13 +685,13 @@ child_spec() = #{id => child_id(), % mandatory
<v>Args = term()</v>
<v>Result = {ok,{SupFlags,[ChildSpec]}} | ignore</v>
<v>&nbsp;SupFlags =
- <seealso marker="#type-sup_flags"><c>sup_flags()</c></seealso></v>
+ <seetype marker="#sup_flags"><c>sup_flags()</c></seetype></v>
<v>&nbsp;ChildSpec =
- <seealso marker="#type-child_spec"><c>child_spec()</c></seealso></v>
+ <seetype marker="#child_spec"><c>child_spec()</c></seetype></v>
</type>
<desc>
<p>Whenever a supervisor is started using
- <seealso marker="#start_link/2"><c>start_link/2,3</c></seealso>,
+ <seemfa marker="#start_link/2"><c>start_link/2,3</c></seemfa>,
this function is called by
the new process to find out about restart strategy, maximum
restart intensity, and child specifications.</p>
@@ -689,8 +702,8 @@ child_spec() = #{id => child_id(), % mandatory
supervisor. <c>[ChildSpec]</c> is a list of valid child
specifications defining which child processes the supervisor
must start and monitor. See the discussion in section
- <seealso marker="#supervision_princ">
- <c>Supervision Principles</c></seealso> earlier.</p>
+ <seeerl marker="#supervision_princ">
+ <c>Supervision Principles</c></seeerl> earlier.</p>
<p>Notice that when the restart strategy is
<c>simple_one_for_one</c>, the list of child specifications
must be a list with one child specification only.
@@ -698,24 +711,24 @@ child_spec() = #{id => child_id(), % mandatory
No child process is then started
during the initialization phase, but all children are assumed
to be started dynamically using
- <seealso marker="#start_child/2"><c>start_child/2</c></seealso>.</p>
+ <seemfa marker="#start_child/2"><c>start_child/2</c></seemfa>.</p>
<p>The function can also return <c>ignore</c>.</p>
<p>Notice that this function can also be called as a part of a code
upgrade procedure. Therefore, the function is not to have any side
effects. For more information about code upgrade of supervisors, see
section
- <seealso marker="doc/design_principles:appup_cookbook#sup">Changing
- a Supervisor</seealso> in OTP Design Principles.</p>
+ <seeguide marker="system/design_principles:appup_cookbook#sup">Changing
+ a Supervisor</seeguide> in OTP Design Principles.</p>
</desc>
</func>
</funcs>
<section>
<title>See Also</title>
- <p><seealso marker="gen_event"><c>gen_event(3)</c></seealso>,
- <seealso marker="gen_statem"><c>gen_statem(3)</c></seealso>,
- <seealso marker="gen_server"><c>gen_server(3)</c></seealso>,
- <seealso marker="sys"><c>sys(3)</c></seealso></p>
+ <p><seeerl marker="gen_event"><c>gen_event(3)</c></seeerl>,
+ <seeerl marker="gen_statem"><c>gen_statem(3)</c></seeerl>,
+ <seeerl marker="gen_server"><c>gen_server(3)</c></seeerl>,
+ <seeerl marker="sys"><c>sys(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/supervisor_bridge.xml b/lib/stdlib/doc/src/supervisor_bridge.xml
index 3280bea610..97d0ae292a 100644
--- a/lib/stdlib/doc/src/supervisor_bridge.xml
+++ b/lib/stdlib/doc/src/supervisor_bridge.xml
@@ -39,15 +39,15 @@
a supervisor and the subsystem. It behaves like a real supervisor to
its own supervisor, but has a different interface than a real
supervisor to the subsystem. For more information, see
- <seealso marker="doc/design_principles:sup_princ">
- Supervisor Behaviour</seealso> in OTP Design Principles.
+ <seeguide marker="system/design_principles:sup_princ">
+ Supervisor Behaviour</seeguide> in OTP Design Principles.
</p>
<p>A supervisor bridge assumes the functions for starting and stopping
the subsystem to be located in a callback module exporting a
predefined set of functions.</p>
- <p>The <seealso marker="sys"><c>sys(3)</c></seealso> module can be used
+ <p>The <seeerl marker="sys"><c>sys(3)</c></seeerl> module can be used
for debugging a supervisor bridge.</p>
<p>Unless otherwise stated, all functions in this module fail if
@@ -75,8 +75,8 @@
<p>If <c><anno>SupBridgeName</anno>={global,<anno>Name</anno>}</c>,
the supervisor bridge is registered globally as
<c><anno>Name</anno></c> using
- <seealso marker="kernel:global#register_name/2">
- <c>global:register_name/2</c></seealso>.</p>
+ <seemfa marker="kernel:global#register_name/2">
+ <c>global:register_name/2</c></seemfa>.</p>
</item>
<item>
<p>If
@@ -86,7 +86,7 @@
<c>Module</c> callback is to export functions
<c>register_name/2</c>, <c>unregister_name/1</c>, and <c>send/2</c>,
which are to behave like the corresponding functions in
- <seealso marker="kernel:global"><c>global</c></seealso>.
+ <seeerl marker="kernel:global"><c>global</c></seeerl>.
Thus, <c>{via,global,GlobalName}</c> is a valid reference.</p>
</item>
</list>
@@ -125,13 +125,14 @@
</func>
</funcs>
- <section>
- <title>Callback Functions</title>
- <p>The following functions must be exported from a
- <c>supervisor_bridge</c> callback module.</p>
- </section>
+
<funcs>
+ <fsdescription>
+ <title>Callback Functions</title>
+ <p>The following functions must be exported from a
+ <c>supervisor_bridge</c> callback module.</p>
+ </fsdescription>
<func>
<name since="">Module:init(Args) -> Result</name>
<fsummary>Initialize process and start subsystem.</fsummary>
@@ -144,7 +145,7 @@
</type>
<desc>
<p>Whenever a supervisor bridge is started using
- <seealso marker="#start_link/2"><c>start_link/2,3</c></seealso>,
+ <seemfa marker="#start_link/2"><c>start_link/2,3</c></seemfa>,
this function is called
by the new process to start the subsystem and initialize.</p>
<p><c>Args</c> is the <c>Args</c> argument provided to the start
@@ -188,8 +189,8 @@
<section>
<title>See Also</title>
- <p><seealso marker="supervisor"><c>supervisor(3)</c></seealso>,
- <seealso marker="sys"><c>sys(3)</c></seealso></p>
+ <p><seeerl marker="supervisor"><c>supervisor(3)</c></seeerl>,
+ <seeerl marker="sys"><c>sys(3)</c></seeerl></p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index ebea054fff..8227be6824 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -41,7 +41,7 @@
understand system messages, such as debug messages and code change. These
functions must be used to implement the use of system messages for a
process; either directly, or through standard behaviors, such as
- <seealso marker="gen_server"><c>gen_server</c></seealso>.</p>
+ <seeerl marker="gen_server"><c>gen_server</c></seeerl>.</p>
<p>The default time-out is 5000 ms, unless otherwise specified.
<c>timeout</c> defines the time to wait for the process to
respond to a request. If the process does not respond, the
@@ -50,8 +50,8 @@
<marker id="dbg_opt"/>
<p>The functions make references to a debug structure.
The debug structure is a list of <c>dbg_opt()</c>, which is an internal
- data type used by function <seealso marker="#handle_system_msg/6">
- <c>handle_system_msg/6</c></seealso>. No debugging is performed if it is
+ data type used by function <seemfa marker="#handle_system_msg/6">
+ <c>handle_system_msg/6</c></seemfa>. No debugging is performed if it is
an empty list.</p>
</description>
@@ -66,8 +66,8 @@
<c>{system, From, Msg}</c>. The content and meaning of
this message are not interpreted by the
receiving process module. When a system message is received, function
- <seealso marker="#handle_system_msg/6">
- <c>handle_system_msg/6</c></seealso>
+ <seemfa marker="#handle_system_msg/6">
+ <c>handle_system_msg/6</c></seemfa>
is called to handle the request.</p>
</item>
<item>
@@ -82,7 +82,7 @@
<item>
<p>If the modules used to implement the process change dynamically
during runtime, the process must understand one more message. An
- example is the <seealso marker="gen_event"><c>gen_event</c></seealso>
+ example is the <seeerl marker="gen_event"><c>gen_event</c></seeerl>
processes. The message is <c>{_Label, {From, Ref}, get_modules}</c>.
The reply to this message is <c>From ! {Ref, Modules}</c>, where
<c>Modules</c> is a list of the currently active modules in the
@@ -332,8 +332,8 @@
provided for convenience, allowing developers to avoid having to
create their own state extraction functions and also avoid having
to interactively extract the state from the return values of
- <seealso marker="#get_status-1"><c>get_status/1</c></seealso> or
- <seealso marker="#get_status-2"><c>get_status/2</c></seealso>
+ <seemfa marker="#get_status/1"><c>get_status/1</c></seemfa> or
+ <seemfa marker="#get_status/2"><c>get_status/2</c></seemfa>
while debugging.</p>
</note>
<p>The value of <c><anno>State</anno></c> varies for different types of
@@ -341,19 +341,19 @@
<list type="bulleted">
<item>
<p>For a
- <seealso marker="gen_server"><c>gen_server</c></seealso>
+ <seeerl marker="gen_server"><c>gen_server</c></seeerl>
process, the returned <c><anno>State</anno></c>
is the state of the callback module.</p>
</item>
<item>
<p>For a
- <seealso marker="gen_statem"><c>gen_statem</c></seealso>
+ <seeerl marker="gen_statem"><c>gen_statem</c></seeerl>
process, <c><anno>State</anno></c> is the tuple
<c>{CurrentState,CurrentData}</c>.</p>
</item>
<item>
<p>For a
- <seealso marker="gen_event"><c>gen_event</c></seealso>
+ <seeerl marker="gen_event"><c>gen_event</c></seeerl>
process, <c><anno>State</anno></c> is a list of tuples,
where each tuple corresponds to an event handler registered
in the process and contains <c>{Module, Id, HandlerState}</c>,
@@ -378,9 +378,9 @@
<p>If the callback module exports a function <c>system_get_state/1</c>,
it is called in the target process to get its state. Its argument is
the same as the <c>Misc</c> value returned by
- <seealso marker="#get_status-1"><c>get_status/1,2</c></seealso>, and
- function <seealso marker="#Module:system_get_state/1">
- <c>Module:system_get_state/1</c></seealso> is expected to extract the
+ <seemfa marker="#get_status/1"><c>get_status/1,2</c></seemfa>, and
+ function <seemfa marker="#Module:system_get_state/1">
+ <c>Module:system_get_state/1</c></seemfa> is expected to extract the
state of the callback module from it. Function
<c>system_get_state/1</c> must return <c>{ok, State}</c>, where
<c>State</c> is the state of the callback module.</p>
@@ -394,14 +394,14 @@
<c>Class</c> and <c>Reason</c> indicate details of the exception.</p>
<p>Function <c>system_get_state/1</c> is primarily useful for
user-defined behaviors and modules that implement OTP
- <seealso marker="#special_process">special processes</seealso>.
+ <seeerl marker="#special_process">special processes</seeerl>.
The <c>gen_server</c>,
<c>gen_statem</c>, and <c>gen_event</c> OTP
behavior modules export this function, so callback modules for those
behaviors need not to supply their own.</p>
<p>For more information about a process, including its state, see
- <seealso marker="#get_status-1"><c>get_status/1</c></seealso> and
- <seealso marker="#get_status-2"><c>get_status/2</c></seealso>.</p>
+ <seemfa marker="#get_status/1"><c>get_status/1</c></seemfa> and
+ <seemfa marker="#get_status/2"><c>get_status/2</c></seemfa>.</p>
</desc>
</func>
@@ -415,16 +415,16 @@
processes, for example:</p>
<list type="bulleted">
<item>
- <p>A <seealso marker="gen_server"><c>gen_server</c></seealso>
+ <p>A <seeerl marker="gen_server"><c>gen_server</c></seeerl>
process returns the state of the callback module.</p>
</item>
<item>
- <p>A <seealso marker="gen_statem"><c>gen_statem</c></seealso>
+ <p>A <seeerl marker="gen_statem"><c>gen_statem</c></seeerl>
process returns information, such as its current
state name and state data.</p>
</item>
<item>
- <p>A <seealso marker="gen_event"><c>gen_event</c></seealso>
+ <p>A <seeerl marker="gen_event"><c>gen_event</c></seeerl>
process returns information about each of its
registered handlers.</p>
</item>
@@ -434,12 +434,12 @@
can also change the value of <c><anno>Misc</anno></c>
by exporting a function <c>format_status/2</c>, which contributes
module-specific information. For details, see
- <seealso marker="gen_server#Module:format_status/2">
- <c>gen_server:format_status/2</c></seealso>,
- <seealso marker="gen_statem#Module:format_status/2">
- <c>gen_statem:format_status/2</c></seealso>, and
- <seealso marker="gen_event#Module:format_status/2">
- <c>gen_event:format_status/2</c></seealso>.</p>
+ <seemfa marker="gen_server#Module:format_status/2">
+ <c>gen_server:format_status/2</c></seemfa>,
+ <seemfa marker="gen_statem#Module:format_status/2">
+ <c>gen_statem:format_status/2</c></seemfa>, and
+ <seemfa marker="gen_event#Module:format_status/2">
+ <c>gen_event:format_status/2</c></seemfa>.</p>
</desc>
</func>
@@ -475,8 +475,8 @@
are printed to <c>standard_io</c>.</p>
<p>The events are formatted with a function that is defined by the
process that generated the event (with a call to
- <seealso marker="#handle_debug/4">
- <c>handle_debug/4</c>)</seealso>.</p>
+ <seemfa marker="#handle_debug/4">
+ <c>handle_debug/4</c>)</seemfa>.</p>
</desc>
</func>
@@ -488,7 +488,7 @@
<p>Enables or disables the logging of all system events in text
format to the file. The events are formatted with a function that is
defined by the process that generated the event (with a call to
- <seealso marker="#handle_debug/4"><c>handle_debug/4</c></seealso>).
+ <seemfa marker="#handle_debug/4"><c>handle_debug/4</c></seemfa>).
The file is opened with encoding UTF-8.</p>
</desc>
</func>
@@ -500,7 +500,7 @@
<desc>
<p>Turns off all debugging for the process. This includes
functions that are installed explicitly with function
- <seealso marker="#install/2"><c>install/2,3</c></seealso>,
+ <seemfa marker="#install/2"><c>install/2,3</c></seemfa>,
for example, triggers.</p>
</desc>
</func>
@@ -535,13 +535,13 @@
processes as follows:</p>
<list type="bulleted">
<item>
- <p>For a <seealso marker="gen_server"><c>gen_server</c></seealso>
+ <p>For a <seeerl marker="gen_server"><c>gen_server</c></seeerl>
process, <c><anno>State</anno></c> is the state of the callback
module and <c><anno>NewState</anno></c>
is a new instance of that state.</p>
</item>
<item>
- <p>For a <seealso marker="gen_statem"><c>gen_statem</c></seealso>
+ <p>For a <seeerl marker="gen_statem"><c>gen_statem</c></seeerl>
process, <c><anno>State</anno></c> is the
tuple <c>{CurrentState,CurrentData}</c>,
and <c><anno>NewState</anno></c> is a
@@ -549,7 +549,7 @@
a new current state, new state data, or both.</p>
</item>
<item>
- <p>For a <seealso marker="gen_event"><c>gen_event</c></seealso>
+ <p>For a <seeerl marker="gen_event"><c>gen_event</c></seeerl>
process, <c><anno>State</anno></c> is the
tuple <c>{Module, Id, HandlerState}</c> as follows:</p>
<taglist>
@@ -591,12 +591,12 @@
succeed in changing the states of other event
handlers registered in the same <c>gen_event</c> process.</p>
<p>If the callback module exports a
- <seealso marker="#Module:system_replace_state/2">
- <c>system_replace_state/2</c></seealso> function, it is called in the
+ <seemfa marker="#Module:system_replace_state/2">
+ <c>system_replace_state/2</c></seemfa> function, it is called in the
target process to replace its state using <c>StateFun</c>. Its two
arguments are <c>StateFun</c> and <c>Misc</c>, where
<c>Misc</c> is the same as the <c>Misc</c> value returned by
- <seealso marker="#get_status-1"><c>get_status/1,2</c></seealso>.
+ <seemfa marker="#get_status/1"><c>get_status/1,2</c></seemfa>.
A <c>system_replace_state/2</c> function is expected to return
<c>{ok, NewState, NewMisc}</c>, where <c>NewState</c> is the new state
of the callback module, obtained by calling <c>StateFun</c>, and
@@ -606,7 +606,7 @@
module within it).</p>
<p>If the callback module does not export a
<c>system_replace_state/2</c> function,
- <seealso marker="#replace_state/2"><c>replace_state/2,3</c></seealso>
+ <seemfa marker="#replace_state/2"><c>replace_state/2,3</c></seemfa>
assumes that <c>Misc</c> is the state of the callback module,
passes it to <c>StateFun</c> and uses the return value as
both the new state and as the new value of <c>Misc</c>.</p>
@@ -621,7 +621,7 @@
<c>{callback_failed, StateFun, {Class, Reason}}</c>.</p>
<p>Function <c>system_replace_state/2</c> is primarily useful for
user-defined behaviors and modules that implement OTP
- <seealso marker="#special_process">special processes</seealso>. The
+ <seeerl marker="#special_process">special processes</seeerl>. The
OTP behavior modules <c>gen_server</c>,
<c>gen_statem</c>, and <c>gen_event</c>
export this function, so callback modules for those
@@ -680,22 +680,23 @@
<p>Prints all system events on <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
- <seealso marker="#handle_debug/4"><c>handle_debug/4</c></seealso>).
+ <seemfa marker="#handle_debug/4"><c>handle_debug/4</c></seemfa>).
</p>
</desc>
</func>
</funcs>
- <section>
- <title>Process Implementation Functions</title>
- <marker id="special_process"/>
- <p>The following functions are used when implementing a
- special process. This is an ordinary process, which does not use a
- standard behavior, but a process that understands the standard system
- messages.</p>
- </section>
+
<funcs>
+ <fsdescription>
+ <title>Process Implementation Functions</title>
+ <marker id="special_process"/>
+ <p>The following functions are used when implementing a
+ special process. This is an ordinary process, which does not use a
+ standard behavior, but a process that understands the standard system
+ messages.</p>
+ </fsdescription>
<func>
<name name="debug_options" arity="1" since=""/>
<fsummary>Convert a list of options to a debug structure.</fsummary>
@@ -785,7 +786,7 @@
<p>Prints the logged system events in the debug structure,
using <c>FormFunc</c> as defined when the event was
generated by a call to
- <seealso marker="#handle_debug/4"><c>handle_debug/4</c></seealso>.</p>
+ <seemfa marker="#handle_debug/4"><c>handle_debug/4</c></seemfa>.</p>
</desc>
</func>
@@ -796,7 +797,7 @@
<p>
Returns the logged system events in the debug structure,
that is the last argument to
- <seealso marker="#handle_debug/4"><c>handle_debug/4</c></seealso>.
+ <seemfa marker="#handle_debug/4"><c>handle_debug/4</c></seemfa>.
</p>
</desc>
</func>
@@ -813,8 +814,8 @@
<v>NMisc = term()</v>
</type>
<desc>
- <p>Called from <seealso marker="#handle_system_msg/6">
- <c>handle_system_msg/6</c></seealso> when the process is to perform a
+ <p>Called from <seemfa marker="#handle_system_msg/6">
+ <c>handle_system_msg/6</c></seemfa> when the process is to perform a
code change. The code change is used when the
internal data structure has changed. This function
converts argument <c>Misc</c> to the new data
@@ -829,12 +830,12 @@
<fsummary>Called when the process is to continue its execution.</fsummary>
<type>
<v>Parent = pid()</v>
- <v>Debug = [<seealso marker="#type-dbg_opt">dbg_opt()</seealso>]</v>
+ <v>Debug = [<seetype marker="#dbg_opt">dbg_opt()</seetype>]</v>
<v>Misc = term()</v>
</type>
<desc>
- <p>Called from <seealso marker="#handle_system_msg/6">
- <c>handle_system_msg/6</c></seealso> when the process is to continue
+ <p>Called from <seemfa marker="#handle_system_msg/6">
+ <c>handle_system_msg/6</c></seemfa> when the process is to continue
its execution (for example, after it has been
suspended). This function never returns.</p>
</desc>
@@ -849,11 +850,11 @@
<v>State = term()</v>
</type>
<desc>
- <p>Called from <seealso marker="#handle_system_msg/6">
- <c>handle_system_msg/6</c></seealso>
+ <p>Called from <seemfa marker="#handle_system_msg/6">
+ <c>handle_system_msg/6</c></seemfa>
when the process is to return a term that reflects its current state.
<c>State</c> is the value returned by
- <seealso marker="#get_state/2"><c>get_state/2</c></seealso>.</p>
+ <seemfa marker="#get_state/2"><c>get_state/2</c></seemfa>.</p>
</desc>
</func>
@@ -869,10 +870,10 @@
<v>NMisc = term()</v>
</type>
<desc>
- <p>Called from <seealso marker="#handle_system_msg/6">
- <c>handle_system_msg/6</c></seealso> when the process is to replace
+ <p>Called from <seemfa marker="#handle_system_msg/6">
+ <c>handle_system_msg/6</c></seemfa> when the process is to replace
its current state. <c>NState</c> is the value returned by
- <seealso marker="#replace_state/3"><c>replace_state/3</c></seealso>.
+ <seemfa marker="#replace_state/3"><c>replace_state/3</c></seemfa>.
</p>
</desc>
</func>
@@ -883,12 +884,12 @@
<type>
<v>Reason = term()</v>
<v>Parent = pid()</v>
- <v>Debug = [<seealso marker="#type-dbg_opt">dbg_opt()</seealso>]</v>
+ <v>Debug = [<seetype marker="#dbg_opt">dbg_opt()</seetype>]</v>
<v>Misc = term()</v>
</type>
<desc>
- <p>Called from <seealso marker="#handle_system_msg/6">
- <c>handle_system_msg/6</c></seealso> when the process is to terminate.
+ <p>Called from <seemfa marker="#handle_system_msg/6">
+ <c>handle_system_msg/6</c></seemfa> when the process is to terminate.
For example, this function is called when
the process is suspended and its parent orders shutdown.
It gives the process a chance to do a cleanup. This function never
diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml
index 165eecfbb0..56342b94be 100644
--- a/lib/stdlib/doc/src/timer.xml
+++ b/lib/stdlib/doc/src/timer.xml
@@ -41,12 +41,18 @@
process.</p>
<p>Successful evaluations of the timer functions give return values
containing a timer reference, denoted <c>TRef</c>. By using
- <seealso marker="#cancel/1"><c>cancel/1</c></seealso>,
+ <seemfa marker="#cancel/1"><c>cancel/1</c></seemfa>,
the returned reference can be used to cancel any
requested action. A <c>TRef</c> is an Erlang term, which contents
must not be changed.</p>
<p>The time-outs are not exact, but are <em>at least</em> as long
as requested.</p>
+ <p>Creating timers using
+ <seemfa marker="erts:erlang#send_after/3">erlang:send_after/3</seemfa> and
+ <seemfa marker="erts:erlang#start_timer/3">erlang:start_timer/3</seemfa>
+ is much more efficient than using the timers provided by this module. See
+ <seeguide marker="system/efficiency_guide:commoncaveats#timer-module">the
+ Timer Module section in the Efficiency Guide</seeguide>.</p>
</description>
<datatypes>
@@ -165,10 +171,10 @@
<anno>T2</anno> - <anno>T1</anno></c> in <em>microseconds</em>,
where <c><anno>T1</anno></c> and <c><anno>T2</anno></c>
are time-stamp tuples on the same format as returned from
- <seealso marker="erts:erlang#timestamp/0">
- <c>erlang:timestamp/0</c></seealso> or
- <seealso marker="kernel:os#timestamp/0">
- <c>os:timestamp/0</c></seealso>.</p>
+ <seemfa marker="erts:erlang#timestamp/0">
+ <c>erlang:timestamp/0</c></seemfa> or
+ <seemfa marker="kernel:os#timestamp/0">
+ <c>os:timestamp/0</c></seemfa>.</p>
</desc>
</func>
@@ -195,6 +201,9 @@
can also be an atom of a registered name.)</p>
<p>Returns <c>{ok, <anno>TRef</anno>}</c> or
<c>{error, <anno>Reason</anno>}</c>.</p>
+ <p>See also
+ <seeguide marker="system/efficiency_guide:commoncaveats#timer-module">
+ the Timer Module section in the Efficiency Guide</seeguide>.</p>
</item>
<tag><c>send_after/2</c></tag>
<item>
@@ -253,7 +262,7 @@
is needed. This is useful during development, but in a
target system the server is to be started explicitly. Use
configuration parameters for
- <seealso marker="kernel:index">Kernel</seealso> for this.</p>
+ <seeapp marker="kernel:index">Kernel</seeapp> for this.</p>
</desc>
</func>
@@ -270,8 +279,8 @@
<item>
<p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
<anno>Arguments</anno>)</c> and measures the elapsed real time as
- reported by <seealso marker="erts:erlang#monotonic_time/0">
- <c>erlang:monotonic_time/0</c></seealso>.</p>
+ reported by <seemfa marker="erts:erlang#monotonic_time/0">
+ <c>erlang:monotonic_time/0</c></seemfa>.</p>
<p>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
@@ -318,27 +327,27 @@ timer:cancel(R),
<section>
<title>Notes</title>
<p>A timer can always be removed by calling
- <seealso marker="#cancel/1"><c>cancel/1</c></seealso>.</p>
+ <seemfa marker="#cancel/1"><c>cancel/1</c></seemfa>.</p>
<p>An interval timer, that is, a timer created by evaluating any of the
functions
- <seealso marker="#apply_interval/4"><c>apply_interval/4</c></seealso>,
- <seealso marker="#send_interval/3"><c>send_interval/3</c></seealso>, and
- <seealso marker="#send_interval/2"><c>send_interval/2</c></seealso>
+ <seemfa marker="#apply_interval/4"><c>apply_interval/4</c></seemfa>,
+ <seemfa marker="#send_interval/3"><c>send_interval/3</c></seemfa>, and
+ <seemfa marker="#send_interval/2"><c>send_interval/2</c></seemfa>
is linked to the process to which the timer performs its task.</p>
<p>A one-shot timer, that is, a timer created by evaluating any of the
functions
- <seealso marker="#apply_after/4"><c>apply_after/4</c></seealso>,
- <seealso marker="#send_after/3"><c>send_after/3</c></seealso>,
- <seealso marker="#send_after/2"><c>send_after/2</c></seealso>,
- <seealso marker="#exit_after/3"><c>exit_after/3</c></seealso>,
- <seealso marker="#exit_after/2"><c>exit_after/2</c></seealso>,
- <seealso marker="#kill_after/2"><c>kill_after/2</c></seealso>, and
- <seealso marker="#kill_after/1"><c>kill_after/1</c></seealso>
+ <seemfa marker="#apply_after/4"><c>apply_after/4</c></seemfa>,
+ <seemfa marker="#send_after/3"><c>send_after/3</c></seemfa>,
+ <seemfa marker="#send_after/2"><c>send_after/2</c></seemfa>,
+ <seemfa marker="#exit_after/3"><c>exit_after/3</c></seemfa>,
+ <seemfa marker="#exit_after/2"><c>exit_after/2</c></seemfa>,
+ <seemfa marker="#kill_after/2"><c>kill_after/2</c></seemfa>, and
+ <seemfa marker="#kill_after/1"><c>kill_after/1</c></seemfa>
is not linked to any process. Hence, such a timer is removed only
when it reaches its time-out, or if it is explicitly removed by a call to
- <seealso marker="#cancel/1"><c>cancel/1</c></seealso>.</p>
+ <seemfa marker="#cancel/1"><c>cancel/1</c></seemfa>.</p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml
index d626f7473d..1f0133de67 100644
--- a/lib/stdlib/doc/src/unicode.xml
+++ b/lib/stdlib/doc/src/unicode.xml
@@ -56,23 +56,23 @@
canonical-equivalent Unicode characters as equal. All characters
should thus be normalized to one form once on the system borders.
One of the following functions can convert characters to their
- normalized forms <seealso marker="#characters_to_nfc_list/1">
- <c>characters_to_nfc_list/1</c></seealso>,
- <seealso marker="#characters_to_nfc_binary/1">
- <c>characters_to_nfc_binary/1</c></seealso>,
- <seealso marker="#characters_to_nfd_list/1">
- <c>characters_to_nfd_list/1</c></seealso> or
- <seealso marker="#characters_to_nfd_binary/1">
- <c>characters_to_nfd_binary/1</c></seealso>.
+ normalized forms <seemfa marker="#characters_to_nfc_list/1">
+ <c>characters_to_nfc_list/1</c></seemfa>,
+ <seemfa marker="#characters_to_nfc_binary/1">
+ <c>characters_to_nfc_binary/1</c></seemfa>,
+ <seemfa marker="#characters_to_nfd_list/1">
+ <c>characters_to_nfd_list/1</c></seemfa> or
+ <seemfa marker="#characters_to_nfd_binary/1">
+ <c>characters_to_nfd_binary/1</c></seemfa>.
For general text
- <seealso marker="#characters_to_nfc_list/1">
- <c>characters_to_nfc_list/1</c></seealso> or
- <seealso marker="#characters_to_nfc_binary/1">
- <c>characters_to_nfc_binary/1</c></seealso> is preferred, and
+ <seemfa marker="#characters_to_nfc_list/1">
+ <c>characters_to_nfc_list/1</c></seemfa> or
+ <seemfa marker="#characters_to_nfc_binary/1">
+ <c>characters_to_nfc_binary/1</c></seemfa> is preferred, and
for identifiers one of the compatibility normalization
functions, such as
- <seealso marker="#characters_to_nfkc_list/1">
- <c>characters_to_nfkc_list/1</c></seealso>,
+ <seemfa marker="#characters_to_nfkc_list/1">
+ <c>characters_to_nfkc_list/1</c></seemfa>,
is preferred for security reasons.
The normalization functions where introduced in OTP 20.
Additional information on normalization can be found in the
@@ -177,8 +177,8 @@
<name name="characters_to_binary" arity="3" since=""/>
<fsummary>Convert a collection of characters to a UTF-8 binary.</fsummary>
<desc>
- <p>Behaves as <seealso marker="#characters_to_list/2">
- <c>characters_to_list/2</c></seealso>, but produces a binary
+ <p>Behaves as <seemfa marker="#characters_to_list/2">
+ <c>characters_to_list/2</c></seemfa>, but produces a binary
instead of a Unicode list.</p>
<p><c><anno>InEncoding</anno></c> defines how input is to be interpreted
if binaries are present in <c>Data</c></p>
@@ -203,8 +203,8 @@
<p>The atoms <c>big</c> and <c>little</c> denote big- or little-endian
encoding.</p>
<p>Errors and exceptions occur as in
- <seealso marker="#characters_to_list/2">
- <c>characters_to_list/2</c></seealso>, but the second element
+ <seemfa marker="#characters_to_list/2">
+ <c>characters_to_list/2</c></seemfa>, but the second element
in tuple <c>error</c> or <c>incomplete</c> is a <c>binary()</c>
and not a <c>list()</c>.</p>
</desc>
@@ -256,8 +256,8 @@
combinations of Unicode characters into a pure Unicode
string in list representation for further processing. For
writing the data to an external entity, the reverse function
- <seealso marker="#characters_to_binary/3">
- <c>characters_to_binary/3</c></seealso>
+ <seemfa marker="#characters_to_binary/3">
+ <c>characters_to_binary/3</c></seemfa>
comes in handy.</p>
<p>Option <c>unicode</c> is an alias for <c>utf8</c>, as this is the
preferred encoding for Unicode characters in
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index 789e063c12..cc90f0dafb 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>2017</year>
+ <year>2020</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -55,8 +55,8 @@
source code, with enhancements to many of the applications to
support both Unicode encoded filenames and support for UTF-8
encoded files in many circumstances. Most notable is the
- support for UTF-8 in files read by <seealso
- marker="kernel:file#consult/1"><c>file:consult/1</c></seealso>,
+ support for UTF-8 in files read by <seemfa
+ marker="kernel:file#consult/1"><c>file:consult/1</c></seemfa>,
release handler support for UTF-8, and more support for
Unicode character sets in the I/O system.</p></item>
@@ -221,7 +221,7 @@
issues occur, which is why UTF-16 exists in both a big-endian
and a little-endian variant.</p>
<p>In Erlang, the full UTF-16 range is supported when applicable, like
- in the <seealso marker="stdlib:unicode"><c>unicode</c></seealso>
+ in the <seeerl marker="stdlib:unicode"><c>unicode</c></seeerl>
module and in the bit syntax.</p>
</item>
<tag>UTF-32</tag>
@@ -278,13 +278,13 @@
<p>The Unicode characters need to be processed by the Erlang
program, which is why library functions must be able to handle
them. In some cases functionality has been added to already
- existing interfaces (as the <seealso
- marker="stdlib:string"><c>string</c></seealso> module now can
+ existing interfaces (as the <seeerl
+ marker="stdlib:string"><c>string</c></seeerl> module now can
handle strings with any code points). In some cases new
- functionality or options have been added (as in the <seealso
- marker="stdlib:io"><c>io</c></seealso> module, the file
- handling, the <seealso
- marker="stdlib:unicode"><c>unicode</c></seealso> module, and
+ functionality or options have been added (as in the <seeerl
+ marker="stdlib:io"><c>io</c></seeerl> module, the file
+ handling, the <seeerl
+ marker="stdlib:unicode"><c>unicode</c></seeerl> module, and
the bit syntax). Today most modules in Kernel and
STDLIB, as well as the VM are Unicode-aware.</p>
</item>
@@ -343,7 +343,7 @@
%% -*- coding: utf-8 -*-</code>
<p>This of course requires your editor to support UTF-8 as well. The
same comment is also interpreted by functions like
- <seealso marker="kernel:file#consult/1"><c>file:consult/1</c></seealso>,
+ <seemfa marker="kernel:file#consult/1"><c>file:consult/1</c></seemfa>,
the release handler, and so on, so that you can have all text files
in your source directories in UTF-8 encoding.</p>
</item>
@@ -382,11 +382,11 @@
<p>Only if a string contains code points &lt; 256, can it be directly
converted to a binary by using, for example,
- <seealso marker="erts:erlang#iolist_to_binary/1"><c>erlang:iolist_to_binary/1</c></seealso>
+ <seemfa marker="erts:erlang#iolist_to_binary/1"><c>erlang:iolist_to_binary/1</c></seemfa>
or can be sent directly to a port. If the string contains Unicode
characters &gt; 255, an encoding must be decided upon and the string is to
be converted to a binary in the preferred encoding using
- <seealso marker="stdlib:unicode#characters_to_binary/1"><c>unicode:characters_to_binary/1,2,3</c></seealso>.
+ <seemfa marker="stdlib:unicode#characters_to_binary/1"><c>unicode:characters_to_binary/1,2,3</c></seemfa>.
Strings are not generally lists of bytes, as they were before
Erlang/OTP R13, they are lists of characters. Characters are not
generally bytes, they are Unicode code points.</p>
@@ -395,7 +395,7 @@
store textual data in binaries instead of lists, mainly because they are
more compact (one byte per character instead of two words per character,
as is the case with lists). Using
- <seealso marker="erts:erlang#list_to_binary/1"><c>erlang:list_to_binary/1</c></seealso>,
+ <seemfa marker="erts:erlang#list_to_binary/1"><c>erlang:list_to_binary/1</c></seemfa>,
an ISO Latin-1 Erlang string can be converted into a binary, effectively
using bytewise encoding: one byte per character. This was convenient for
those limited Erlang strings, but cannot be done for arbitrary Unicode
@@ -428,7 +428,7 @@ chardata() = charlist() | unicode_binary()
charlist() = maybe_improper_list(char() | unicode_binary() | charlist(),
unicode_binary() | nil())</code>
- <p>The module <seealso marker="stdlib:unicode"><c>unicode</c></seealso>
+ <p>The module <seeerl marker="stdlib:unicode"><c>unicode</c></seeerl>
even supports similar mixes with binaries containing other encodings than
UTF-8, but that is a special case to allow for conversions to and from
external data:</p>
@@ -448,8 +448,8 @@ external_charlist() = maybe_improper_list(char() | external_unicode_binary() |
<p><marker id="unicode_in_erlang"/>As from Erlang/OTP R16, Erlang
source files can be written in UTF-8 or bytewise (<c>latin1</c>)
encoding. For information about how to state the encoding of an
- Erlang source file, see the <seealso
- marker="stdlib:epp#encoding"><c>epp(3)</c></seealso> module. As
+ Erlang source file, see the <seeerl
+ marker="stdlib:epp#encoding"><c>epp(3)</c></seeerl> module. As
from Erlang/OTP R16, strings and comments can be written using
Unicode. As from Erlang/OTP 20, also atoms and functions can be
written using Unicode. Modules, applications, and nodes must still be
@@ -578,8 +578,8 @@ Eshell V5.10.1 (abort with ^G)
bytewise encoded) as string data.</p>
<p>These heuristics are also used by
- <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>,
- <seealso marker="stdlib:io_lib#format/2"><c>io_lib:format/2</c></seealso>,
+ <seemfa marker="stdlib:io#format/2"><c>io:format/2</c></seemfa>,
+ <seemfa marker="stdlib:io_lib#format/2"><c>io_lib:format/2</c></seemfa>,
and friends when modifier <c>t</c> is used with <c>~p</c> or
<c>~P</c>:</p>
@@ -644,7 +644,7 @@ en_US.UTF-8</pre>
language and character type settings.</p>
<p>To investigate what Erlang thinks about the terminal, the call
- <seealso marker="stdlib:io#getopts/1"><c>io:getopts()</c></seealso>
+ <seemfa marker="stdlib:io#getopts/1"><c>io:getopts()</c></seemfa>
can be used when the shell is started:</p>
<pre>
@@ -728,9 +728,9 @@ Eshell V5.10.1 (abort with ^G)
<taglist>
<tag>Mandatory Unicode file naming</tag>
<item>
- <p>Windows and, for most common uses, MacOS X enforce Unicode support
- for filenames. All files created in the file system have names that
- can consistently be interpreted. In MacOS X, all filenames are
+ <p>Windows, Android and, for most cases, MacOS X enforce Unicode support
+ for filenames. All files created in the file system have names that can
+ consistently be interpreted. In MacOS X and Android, all filenames are
retrieved in UTF-8 encoding. In Windows, each system call handling
filenames has a special Unicode-aware variant, giving much the same
effect. There are no filenames on these systems that are not Unicode
@@ -740,7 +740,7 @@ Eshell V5.10.1 (abort with ^G)
translated to the proper name encoding for the underlying operating
system and file system.</p>
<p>Doing, for example, a
- <seealso marker="kernel:file#list_dir/1"><c>file:list_dir/1</c></seealso>
+ <seemfa marker="kernel:file#list_dir/1"><c>file:list_dir/1</c></seemfa>
on one of these systems can return Unicode lists with code points
&gt; 255, depending on the content of the file system.</p>
</item>
@@ -759,7 +759,7 @@ Eshell V5.10.1 (abort with ^G)
<p>In <c>latin1</c> mode, filenames are bytewise encoded. This allows
for list representation of all filenames in the system. However, a
a file named "Östersund.txt", appears in
- <seealso marker="kernel:file#list_dir/1"><c>file:list_dir/1</c></seealso>
+ <seemfa marker="kernel:file#list_dir/1"><c>file:list_dir/1</c></seemfa>
either as "Östersund.txt" (if the filename was encoded in bytewise
ISO Latin-1 by the program creating the file) or more probably as
<c>[195,150,115,116,101,114,115,117,110,100]</c>, which is a list
@@ -767,7 +767,7 @@ Eshell V5.10.1 (abort with ^G)
filename translation on such a system, non-UTF-8 filenames are
ignored by functions like <c>file:list_dir/1</c>. They can be
retrieved with function
- <seealso marker="kernel:file#list_dir_all/1"><c>file:list_dir_all/1</c></seealso>,
+ <seemfa marker="kernel:file#list_dir_all/1"><c>file:list_dir_all/1</c></seemfa>,
but wrongly encoded filenames appear as &quot;raw filenames&quot;.
</p>
</item>
@@ -802,14 +802,14 @@ Eshell V5.10.1 (abort with ^G)
<p>Unicode filename translation is turned on with switch <c>+fnu</c>. On
Linux, a VM started without explicitly stating the filename translation
mode defaults to <c>latin1</c> as the native filename encoding. On
- Windows and MacOS X, the default behavior is that of Unicode filename
- translation. Therefore
- <seealso marker="kernel:file#native_name_encoding/0"><c>file:native_name_encoding/0</c></seealso>
+ Windows, MacOS X and Android, the default behavior is that of Unicode
+ filename translation. Therefore
+ <seemfa marker="kernel:file#native_name_encoding/0"><c>file:native_name_encoding/0</c></seemfa>
by default returns <c>utf8</c> on those systems (Windows does not use
UTF-8 on the file system level, but this can safely be ignored by the
Erlang programmer). The default behavior can, as stated earlier, be
changed using option <c>+fnu</c> or <c>+fnl</c> to the VM, see the
- <seealso marker="erts:erl"><c>erl</c></seealso> program. If the VM is
+ <seecom marker="erts:erl"><c>erl</c></seecom> program. If the VM is
started in Unicode filename translation mode,
<c>file:native_name_encoding/0</c> returns atom <c>utf8</c>. Switch
<c>+fnu</c> can be followed by <c>w</c>, <c>i</c>, or <c>e</c> to control
@@ -832,7 +832,7 @@ Eshell V5.10.1 (abort with ^G)
</list>
<p>Notice that
- <seealso marker="kernel:file#read_link/1"><c>file:read_link/1</c></seealso>
+ <seemfa marker="kernel:file#read_link/1"><c>file:read_link/1</c></seemfa>
always returns an error if the link points to an invalid filename.</p>
<p>In Unicode filename mode, filenames given to BIF <c>open_port/2</c> with
@@ -840,7 +840,7 @@ Eshell V5.10.1 (abort with ^G)
is the parameter list specified in option <c>args</c> available when
using <c>spawn_executable</c>. The UTF-8 translation of arguments can be
avoided using binaries, see section
- <seealso marker="#notes-about-raw-filenames">Notes About Raw Filenames</seealso>.
+ <seeguide marker="#notes-about-raw-filenames">Notes About Raw Filenames</seeguide>.
</p>
<p>Notice that the file encoding options specified when opening a file has
@@ -876,7 +876,7 @@ Eshell V5.10.1 (abort with ^G)
therefore expects UTF-8 file naming). The ISO Latin-1 name is not valid
UTF-8 and one can be tempted to think that automatic conversion in, for
example,
- <seealso marker="kernel:file#list_dir/1"><c>file:list_dir/1</c></seealso>
+ <seemfa marker="kernel:file#list_dir/1"><c>file:list_dir/1</c></seemfa>
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 filename)? The VM converts the filename to UTF-8, as this is
@@ -892,7 +892,7 @@ Eshell V5.10.1 (abort with ^G)
are invalid under the encoding. By the common function
<c>file:list_dir/1</c>, the wrongly encoded filenames are ignored in
Unicode filename translation mode, but by function
- <seealso marker="kernel:file#list_dir_all/1"><c>file:list_dir_all/1</c></seealso>
+ <seemfa marker="kernel:file#list_dir_all/1"><c>file:list_dir_all/1</c></seemfa>
the filenames with invalid encoding are returned as &quot;raw&quot;
filenames, that is, as binaries.</p>
@@ -964,9 +964,9 @@ Eshell V5.10.1 (abort with ^G)
Unicode.</p>
<p>If Unicode filenames are enabled, the calls to
- <seealso marker="kernel:os#getenv/0"><c>os:getenv/0,1</c></seealso>,
- <seealso marker="kernel:os#putenv/2"><c>os:putenv/2</c></seealso>, and
- <seealso marker="kernel:os#unsetenv/1"><c>os:unsetenv/1</c></seealso>
+ <seemfa marker="kernel:os#getenv/0"><c>os:getenv/0,1</c></seemfa>,
+ <seemfa marker="kernel:os#putenv/2"><c>os:putenv/2</c></seemfa>, and
+ <seemfa marker="kernel:os#unsetenv/1"><c>os:unsetenv/1</c></seemfa>
handle Unicode strings. On Unix-like platforms, the built-in functions
translate environment variables in UTF-8 to/from Unicode strings, possibly
with code points &gt; 255. On Windows, the Unicode versions of the
@@ -982,8 +982,8 @@ Eshell V5.10.1 (abort with ^G)
non-textual or byte-oriented data (such as <c>gen_tcp</c>).</p>
<p>Modules handling textual data (such as
- <seealso marker="stdlib:io_lib"><c>io_lib</c></seealso> and
- <seealso marker="stdlib:string"><c>string</c></seealso> are sometimes
+ <seeerl marker="stdlib:io_lib"><c>io_lib</c></seeerl> and
+ <seeerl marker="stdlib:string"><c>string</c></seeerl> are sometimes
subject to conversion or extension to be able to handle Unicode
characters.</p>
@@ -997,7 +997,7 @@ Eshell V5.10.1 (abort with ^G)
<taglist>
<tag><c>unicode</c></tag>
<item>
- <p>The <seealso marker="stdlib:unicode"><c>unicode</c></seealso>
+ <p>The <seeerl marker="stdlib:unicode"><c>unicode</c></seeerl>
module is clearly Unicode-aware. It contains functions for conversion
between different Unicode formats and some utilities for identifying
byte order marks. Few programs handling Unicode data survive without
@@ -1005,7 +1005,7 @@ Eshell V5.10.1 (abort with ^G)
</item>
<tag><c>io</c></tag>
<item>
- <p>The <seealso marker="stdlib:io"><c>io</c></seealso> module has been
+ <p>The <seeerl marker="stdlib:io"><c>io</c></seeerl> module has been
extended along with the actual I/O protocol to handle Unicode data.
This means that many functions require binaries to be in UTF-8, and
there are modifiers to format control sequences to allow for output
@@ -1016,36 +1016,36 @@ Eshell V5.10.1 (abort with ^G)
<p>I/O-servers throughout the system can handle Unicode data and have
options for converting data upon output or input to/from the device.
As shown earlier, the
- <seealso marker="stdlib:shell"><c>shell</c></seealso> module has
+ <seeerl marker="stdlib:shell"><c>shell</c></seeerl> module has
support for Unicode terminals and the
- <seealso marker="kernel:file"><c>file</c></seealso> module
+ <seeerl marker="kernel:file"><c>file</c></seeerl> module
allows for translation to and from various Unicode formats on
disk.</p>
<p>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
best read or written using the
- <seealso marker="stdlib:io"><c>io</c></seealso> module.</p>
+ <seeerl marker="stdlib:io"><c>io</c></seeerl> module.</p>
</item>
<tag><c>re</c></tag>
<item>
- <p>The <seealso marker="stdlib:re"><c>re</c></seealso> module allows
+ <p>The <seeerl marker="stdlib:re"><c>re</c></seeerl> module allows
for matching Unicode strings as a special option. As the library is
centered on matching in binaries, the Unicode support is
UTF-8-centered.</p>
</item>
<tag><c>wx</c></tag>
<item>
- <p>The graphical library <seealso marker="wx:wx"><c>wx</c></seealso>
+ <p>The graphical library <seeerl marker="wx:wx"><c>wx</c></seeerl>
has extensive support for Unicode text.</p></item>
</taglist>
- <p>The <seealso marker="stdlib:string"><c>string</c></seealso>
+ <p>The <seeerl marker="stdlib:string"><c>string</c></seeerl>
module works perfectly for Unicode strings and ISO Latin-1
- strings, except the language-dependent functions <seealso
- marker="stdlib:string#uppercase/1"><c>string:uppercase/1</c></seealso>
- and <seealso
- marker="stdlib:string#lowercase/1"><c>string:lowercase/1</c></seealso>.
+ strings, except the language-dependent functions <seemfa
+ marker="stdlib:string#uppercase/1"><c>string:uppercase/1</c></seemfa>
+ and <seemfa
+ marker="stdlib:string#lowercase/1"><c>string:lowercase/1</c></seemfa>.
These two functions can never function correctly for Unicode
characters in their current form, as there are language and locale
issues to consider when converting text between cases. Converting
@@ -1074,9 +1074,9 @@ Eshell V5.10.1 (abort with ^G)
terminal or pipe.</p>
<p>A file can have an encoding option that makes it generally usable by the
- <seealso marker="stdlib:io"><c>io</c></seealso> module (for example
+ <seeerl marker="stdlib:io"><c>io</c></seeerl> module (for example
<c>{encoding,utf8}</c>), but is by default opened as a byte-oriented file.
- The <seealso marker="kernel:file"><c>file</c></seealso> module is
+ The <seeerl marker="kernel:file"><c>file</c></seeerl> module is
byte-oriented, so only ISO Latin-1 characters can be written using that
module. Use the <c>io</c> module if Unicode data is to be output to a
file with other <c>encoding</c> than <c>latin1</c> (bytewise encoding).
@@ -1088,7 +1088,7 @@ Eshell V5.10.1 (abort with ^G)
as that is the way to access files other than text files, byte by byte.
As with ports, you can write encoded data into a file by "manually"
converting the data to the encoding of choice (using the
- <seealso marker="stdlib:unicode"><c>unicode</c></seealso> module or the
+ <seeerl marker="stdlib:unicode"><c>unicode</c></seeerl> module or the
bit syntax) and then output it on a bytewise (<c>latin1</c>) encoded
file.</p>
@@ -1096,10 +1096,10 @@ Eshell V5.10.1 (abort with ^G)
<list type="bulleted">
<item><p>Use the
- <seealso marker="kernel:file"><c>file</c></seealso> module for
+ <seeerl marker="kernel:file"><c>file</c></seeerl> module for
files opened for bytewise access (<c>{encoding,latin1}</c>).</p>
</item>
- <item><p>Use the <seealso marker="stdlib:io"><c>io</c></seealso> module
+ <item><p>Use the <seeerl marker="stdlib:io"><c>io</c></seeerl> module
when accessing files with any other encoding (for example
<c>{encoding,uf8}</c>).</p>
</item>
@@ -1147,31 +1147,31 @@ ok
is started with flag <c>+fna</c> (which is default from
Erlang/OTP 17.0).</p>
<p>You can check the setting of this by calling
- <seealso marker="stdlib:io#getopts/1"><c>io:getopts()</c></seealso>,
+ <seemfa marker="stdlib:io#getopts/1"><c>io:getopts()</c></seemfa>,
which gives you an option list containing <c>{encoding,unicode}</c>
or <c>{encoding,latin1}</c>.</p>
</item>
<tag>The <c>+pc</c> {<c>unicode</c>|<c>latin1</c>} flag to
- <seealso marker="erts:erl"><c>erl(1)</c></seealso></tag>
+ <seecom marker="erts:erl"><c>erl(1)</c></seecom></tag>
<item>
<p>This flag affects what is interpreted as string data when doing
heuristic string detection in the shell and in
- <seealso marker="stdlib:io"><c>io</c></seealso>/
- <seealso marker="stdlib:io_lib#format/2"><c>io_lib:format</c></seealso>
+ <seeerl marker="stdlib:io"><c>io</c></seeerl>/
+ <seemfa marker="stdlib:io_lib#format/2"><c>io_lib:format</c></seemfa>
with the <c>"~tp"</c> and <c>~tP</c> formatting instructions, as
described earlier.</p>
<p>You can check this option by calling
- <seealso marker="stdlib:io#printable_range/0"><c>io:printable_range/0</c></seealso>,
+ <seemfa marker="stdlib:io#printable_range/0"><c>io:printable_range/0</c></seemfa>,
which returns <c>unicode</c> or <c>latin1</c>. To be compatible with
future (expected) extensions to the settings, rather use
- <seealso marker="stdlib:io_lib#printable_list/1"><c>io_lib:printable_list/1</c></seealso>
+ <seemfa marker="stdlib:io_lib#printable_list/1"><c>io_lib:printable_list/1</c></seemfa>
to check if a list is printable according to the setting. That
function takes into account new possible settings returned from
<c>io:printable_range/0</c>.</p>
</item>
<tag>The <c>+fn</c>{<c>l</c>|<c>u</c>|<c>a</c>}
[{<c>w</c>|<c>i</c>|<c>e</c>}] flag to
- <seealso marker="erts:erl"><c>erl(1)</c></seealso></tag>
+ <seecom marker="erts:erl"><c>erl(1)</c></seecom></tag>
<item>
<p>This flag affects how the filenames are to be interpreted. On
operating systems with transparent file naming, this must be
@@ -1199,10 +1199,10 @@ ok
</item>
</list>
<p>The filename translation mode can be read with function
- <seealso marker="kernel:file#native_name_encoding/0"><c>file:native_name_encoding/0</c></seealso>,
+ <seemfa marker="kernel:file#native_name_encoding/0"><c>file:native_name_encoding/0</c></seemfa>,
which returns <c>latin1</c> (bytewise encoding) or <c>utf8</c>.</p>
</item>
- <tag><seealso marker="stdlib:epp#default_encoding/0"><c>epp:default_encoding/0</c></seealso></tag>
+ <tag><seemfa marker="stdlib:epp#default_encoding/0"><c>epp:default_encoding/0</c></seemfa></tag>
<item>
<p>This function returns the default encoding for Erlang source files
(if no encoding comment is present) in the currently running release.
@@ -1210,10 +1210,10 @@ ok
As from Erlang/OTP 17.0, <c>utf8</c> is returned.</p>
<p>The encoding of each file can be specified using comments as
described in the
- <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso> module.
+ <seeerl marker="stdlib:epp#encoding"><c>epp(3)</c></seeerl> module.
</p>
</item>
- <tag><seealso marker="stdlib:io#setopts/1"><c>io:setopts/1,2</c></seealso>
+ <tag><seemfa marker="stdlib:io#setopts/1"><c>io:setopts/1,2</c></seemfa>
and flags <c>-oldshell</c>/<c>-noshell</c></tag>
<item>
<p>When Erlang is started with <c>-oldshell</c> or <c>-noshell</c>, the
@@ -1221,7 +1221,7 @@ ok
encoding, while an interactive shell defaults to what the
environment variables says.</p>
<p>You can set the encoding of a file or other I/O server with function
- <seealso marker="stdlib:io#setopts/1"><c>io:setopts/2</c></seealso>.
+ <seemfa marker="stdlib:io#setopts/1"><c>io:setopts/2</c></seemfa>.
This can also be set when opening a file. Setting the terminal (or
other <c>standard_io</c> server) unconditionally to option
<c>{encoding,utf8}</c> implies that UTF-8 encoded characters are
@@ -1231,7 +1231,7 @@ ok
writing or reading text files in a known encoding.</p>
<p>You can retrieve the <c>encoding</c> setting for an I/O server with
function
- <seealso marker="stdlib:io#getopts/1"><c>io:getopts()</c></seealso>.
+ <seemfa marker="stdlib:io#getopts/1"><c>io:getopts()</c></seemfa>.
</p>
</item>
</taglist>
@@ -1251,7 +1251,7 @@ ok
text. This code outlines how to open a file that is believed to
have a BOM, and sets the files encoding and position for further
sequential reading (preferably using the
- <seealso marker="stdlib:io"><c>io</c></seealso> module).</p>
+ <seeerl marker="stdlib:io"><c>io</c></seeerl> module).</p>
<p>Notice that error handling is omitted from the code:</p>
@@ -1265,12 +1265,12 @@ open_bom_file_for_reading(File) -&gt;
{ok,F}.</code>
<p>Function
- <seealso marker="stdlib:unicode#bom_to_encoding/1"><c>unicode:bom_to_encoding/1</c></seealso>
+ <seemfa marker="stdlib:unicode#bom_to_encoding/1"><c>unicode:bom_to_encoding/1</c></seemfa>
identifies the encoding from a binary of at least four bytes. It
returns, along with a term suitable for setting the encoding of the
file, the byte length of the BOM, so that the file position can be set
accordingly. Notice that function
- <seealso marker="kernel:file#position/2"><c>file:position/2</c></seealso>
+ <seemfa marker="kernel:file#position/2"><c>file:position/2</c></seemfa>
always works on byte-offsets, so that the byte length of the BOM is
needed.</p>
@@ -1284,7 +1284,7 @@ open_bom_file_for_writing(File,Encoding) -&gt;
{ok,F}.</code>
<p>The file is in both these cases then best processed using the
- <seealso marker="stdlib:io"><c>io</c></seealso> module, as the functions
+ <seeerl marker="stdlib:io"><c>io</c></seeerl> module, as the functions
in that module can handle code points beyond the ISO Latin-1 range.</p>
</section>
@@ -1293,8 +1293,8 @@ open_bom_file_for_writing(File,Encoding) -&gt;
<p>When reading and writing to Unicode-aware entities, like a
file opened for Unicode translation, you probably want to format text
strings using the functions in the
- <seealso marker="stdlib:io"><c>io</c></seealso> module or the
- <seealso marker="stdlib:io_lib"><c>io_lib</c></seealso> module. For
+ <seeerl marker="stdlib:io"><c>io</c></seeerl> module or the
+ <seeerl marker="stdlib:io_lib"><c>io_lib</c></seeerl> module. For
backward compatibility reasons, these functions do not accept any list
as a string, but require a special <em>translation modifier</em> when
working with Unicode texts. The modifier is <c>t</c>. When applied to
@@ -1324,11 +1324,11 @@ ok</pre>
bytewise-encoded characters and not UTF-8.</p>
<p>Function
- <seealso marker="stdlib:io_lib#format/2"><c>io_lib:format/2</c></seealso>
+ <seemfa marker="stdlib:io_lib#format/2"><c>io_lib:format/2</c></seemfa>
behaves similarly. It is defined to return a deep list of characters
and the output can easily be converted to binary data for outputting on
any device by a simple
- <seealso marker="erts:erlang#list_to_binary/1"><c>erlang:list_to_binary/1</c></seealso>.
+ <seemfa marker="erts:erlang#list_to_binary/1"><c>erlang:list_to_binary/1</c></seemfa>.
When the translation modifier is used, the list can, however, contain
characters that cannot be stored in one byte. The call to
<c>erlang:list_to_binary/1</c> then fails. However, if the I/O server
@@ -1350,7 +1350,7 @@ ok</pre>
as such, as the Erlang shell uses the Unicode encoding (and is started
with all Unicode characters considered printable). The Unicode list is
valid input to function
- <seealso marker="stdlib:io#put_chars/2"><c>io:put_chars/2</c></seealso>,
+ <seemfa marker="stdlib:io#put_chars/2"><c>io:put_chars/2</c></seemfa>,
so data can be output on any Unicode-capable device. If the device is a
terminal, characters are output in format <c>\x{</c>H...<c>}</c> if
encoding is <c>latin1</c>. Otherwise in UTF-8 (for the non-interactive
@@ -1373,7 +1373,7 @@ ok</pre>
the 7-bit ASCII range are seldom considered valid when decoded as UTF-8.
Therefore one can usually use heuristics to determine if a file is in
UTF-8 or if it is encoded in ISO Latin-1 (one byte per character).
- The <seealso marker="stdlib:unicode"><c>unicode</c></seealso>
+ The <seeerl marker="stdlib:unicode"><c>unicode</c></seeerl>
module can be used to determine if data can be interpreted as UTF-8:</p>
<code>
@@ -1388,7 +1388,7 @@ heuristic_encoding_bin(Bin) when is_binary(Bin) -&gt;
<p>If you do not have a complete binary of the file content, you can
instead chunk through the file and check part by part. The return-tuple
<c>{incomplete,Decoded,Rest}</c> from function
- <seealso marker="stdlib:unicode#characters_to_binary/1"><c>unicode:characters_to_binary/1,2,3</c></seealso>
+ <seemfa marker="stdlib:unicode#characters_to_binary/1"><c>unicode:characters_to_binary/1,2,3</c></seemfa>
comes in handy. The incomplete rest from one chunk of data read from the
file is prepended to the next chunk and we therefore avoid the problem
of character boundaries when reading chunks of bytes in UTF-8
@@ -1415,7 +1415,7 @@ loop_through_file(F,Acc,{ok,Bin}) when is_binary(Bin) -&gt;
<p>Another option is to try to read the whole file in UTF-8 encoding and
see if it fails. Here we need to read the file using function
- <seealso marker="stdlib:io#get_chars/3"><c>io:get_chars/3</c></seealso>,
+ <seemfa marker="stdlib:io#get_chars/3"><c>io:get_chars/3</c></seemfa>,
as we have to read characters with a code point &gt; 255:</p>
<code>
@@ -1454,7 +1454,7 @@ utf8_list_to_string(StrangeList) ->
second time is encoded as UTF-8. A common situation is where you read a
file, byte by byte, but the content is already UTF-8. If you then
convert the bytes to UTF-8, using, for example, the
- <seealso marker="stdlib:unicode"><c>unicode</c></seealso> module, or by
+ <seeerl marker="stdlib:unicode"><c>unicode</c></seeerl> module, or by
writing to a file opened with option <c>{encoding,utf8}</c>, you have
each <em>byte</em> in the input file encoded as UTF-8, not each
character of the original text (one character can have been encoded in
diff --git a/lib/stdlib/doc/src/uri_string.xml b/lib/stdlib/doc/src/uri_string.xml
index 148accf107..dea8e60979 100644
--- a/lib/stdlib/doc/src/uri_string.xml
+++ b/lib/stdlib/doc/src/uri_string.xml
@@ -65,24 +65,27 @@
<p>The functions implemented by this module cover the following use cases:</p>
<list type="bulleted">
<item>Parsing URIs into its components and returing a map<br></br>
- <seealso marker="#parse/1"><c>parse/1</c></seealso>
+ <seemfa marker="#parse/1"><c>parse/1</c></seemfa>
</item>
<item>Recomposing a map of URI components into a URI string<br></br>
- <seealso marker="#recompose/1"><c>recompose/1</c></seealso>
+ <seemfa marker="#recompose/1"><c>recompose/1</c></seemfa>
</item>
<item>Changing inbound binary and percent-encoding of URIs<br></br>
- <seealso marker="#transcode/2"><c>transcode/2</c></seealso>
+ <seemfa marker="#transcode/2"><c>transcode/2</c></seemfa>
</item>
<item>Transforming URIs into a normalized form<br></br>
- <seealso marker="#normalize/1"><c>normalize/1</c></seealso><br></br>
- <seealso marker="#normalize/2"><c>normalize/2</c></seealso>
+ <seemfa marker="#normalize/1"><c>normalize/1</c></seemfa><br></br>
+ <seemfa marker="#normalize/2"><c>normalize/2</c></seemfa>
</item>
<item>Composing form-urlencoded query strings from a list of key-value pairs<br></br>
- <seealso marker="#compose_query/1"><c>compose_query/1</c></seealso><br></br>
- <seealso marker="#compose_query/2"><c>compose_query/2</c></seealso>
+ <seemfa marker="#compose_query/1"><c>compose_query/1</c></seemfa><br></br>
+ <seemfa marker="#compose_query/2"><c>compose_query/2</c></seemfa>
</item>
<item>Dissecting form-urlencoded query strings into a list of key-value pairs<br></br>
- <seealso marker="#dissect_query/1"><c>dissect_query/1</c></seealso>
+ <seemfa marker="#dissect_query/1"><c>dissect_query/1</c></seemfa>
+ </item>
+ <item>Decoding percent-encoded triplets<br></br>
+ <seemfa marker="#percent_decode/1"><c>percent_decode/1</c></seemfa>
</item>
</list>
<p>There are four different encodings present during the handling of URIs:</p>
@@ -150,6 +153,21 @@
<funcs>
<func>
+ <name name="allowed_characters" arity="0" since="OTP 23.2"/>
+ <fsummary>Print allowed characters in URI components.</fsummary>
+ <desc>
+ <p>This is a utility function meant to be used in the shell for printing
+ the allowed characters in each
+ major URI component, and also in the most important characters sets.
+ Please note that this function does not replace the ABNF rules defined by
+ the standards, these character sets are derived directly from those
+ aformentioned rules. For more information see the
+ <seeguide marker="uri_string_usage#percent_encoding">Uniform Resource
+ Identifiers</seeguide> chapter in stdlib's Users Guide.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="compose_query" arity="1" since="OTP 21.0"/>
<fsummary>Compose urlencoded query string.</fsummary>
<desc>
@@ -161,8 +179,8 @@
<url href="https://www.w3.org/TR/html50/">HTML 5.0</url> specification for
non-UTF-8 encodings.
</p>
- <p>See also the opposite operation <seealso marker="#dissect_query/1">
- <c>dissect_query/1</c></seealso>.
+ <p>See also the opposite operation <seemfa marker="#dissect_query/1">
+ <c>dissect_query/1</c></seemfa>.
</p>
<p><em>Example:</em></p>
<pre>
@@ -194,8 +212,8 @@
0x61 to 0x7A, are percent-encoded (U+0025 PERCENT SIGN character (%) followed by
uppercase ASCII hex digits representing the hexadecimal value of the byte).
</p>
- <p>See also the opposite operation <seealso marker="#dissect_query/1">
- <c>dissect_query/1</c></seealso>.
+ <p>See also the opposite operation <seemfa marker="#dissect_query/1">
+ <c>dissect_query/1</c></seemfa>.
</p>
<p><em>Example:</em></p>
<pre>
@@ -221,8 +239,8 @@
<url href="https://www.w3.org/TR/html50/">HTML 5.0</url> specification for
non-UTF-8 encodings.
</p>
- <p>See also the opposite operation <seealso marker="#compose_query/1">
- <c>compose_query/1</c></seealso>.
+ <p>See also the opposite operation <seemfa marker="#compose_query/1">
+ <c>compose_query/1</c></seemfa>.
</p>
<p><em>Example:</em></p>
<pre>
@@ -292,8 +310,8 @@
compliant <c>uri_string()</c> into a <c>uri_map()</c>, that holds the parsed
components of the <c>URI</c>.
If parsing fails, an error tuple is returned.</p>
- <p>See also the opposite operation <seealso marker="#recompose/1">
- <c>recompose/1</c></seealso>.</p>
+ <p>See also the opposite operation <seemfa marker="#recompose/1">
+ <c>recompose/1</c></seemfa>.</p>
<p><em>Example:</em></p>
<pre>
1> <input>uri_string:parse("foo://user@example.com:8042/over/there?name=ferret#nose").</input>
@@ -309,6 +327,37 @@
</func>
<func>
+ <name name="percent_decode" arity="1" since="OTP 23.2"/>
+ <fsummary>Decode percent-decode triplets in the input.</fsummary>
+ <desc>
+ <p>Decodes all percent-encoded triplets in the input that can be both a
+ <c>uri_string()</c> and a <c>uri_map()</c>. Note, that this function performs
+ raw decoding and it shall be used on already parsed URI components. Applying
+ this function directly on a standard URI can effectively change it.</p>
+ <p>If the input encoding is not UTF-8, an error tuple is returned.</p>
+ <p><em>Example:</em></p>
+ <pre>
+1> <input>uri_string:percent_decode(#{host => "localhost-%C3%B6rebro",path => [],</input>
+1> <input>scheme => "http"}).</input>
+#{host => "localhost-örebro",path => [],scheme => "http"}
+2> <![CDATA[uri_string:percent_decode(<<"%C3%B6rebro">>).]]>
+<![CDATA[<<"örebro"/utf8>>]]>
+ </pre>
+ <warning><p>
+ Using <c>uri_string:percent_decode/1</c> directly on a URI is not safe. This
+ example shows, that after each consecutive application of the function
+ the resulting URI will be changed. None of these URIs refer to the same
+ resource.</p>
+ <pre>
+<![CDATA[3> uri_string:percent_decode(<<"http://local%252Fhost/path">>).
+<<"http://local%2Fhost/path">>
+4> uri_string:percent_decode(<<"http://local%2Fhost/path">>).
+<<"http://local/host/path">>]]>
+ </pre></warning>
+ </desc>
+ </func>
+
+ <func>
<name name="recompose" arity="1" since="OTP 21.0"/>
<fsummary>Recompose URI.</fsummary>
<desc>
@@ -316,15 +365,15 @@
<c><anno>URIString</anno></c> (percent-encoded), based on the components of
<c><anno>URIMap</anno></c>.
If the <c><anno>URIMap</anno></c> is invalid, an error tuple is returned.</p>
- <p>See also the opposite operation <seealso marker="#parse/1">
- <c>parse/1</c></seealso>.</p>
+ <p>See also the opposite operation <seemfa marker="#parse/1">
+ <c>parse/1</c></seemfa>.</p>
<p><em>Example:</em></p>
<pre>
1> <input>URIMap = #{fragment => "nose", host => "example.com", path => "/over/there",</input>
1> port => 8042, query => "name=ferret", scheme => "foo", userinfo => "user"}.
-#{fragment => "top",host => "example.com",
- path => "/over/there",port => 8042,query => "?name=ferret",
- scheme => foo,userinfo => "user"}
+#{fragment => "nose",host => "example.com",
+ path => "/over/there",port => 8042,query => "name=ferret",
+ scheme => "foo",userinfo => "user"}
2> <input>uri_string:recompose(URIMap).</input>
"foo://example.com:8042/over/there?name=ferret#nose"</pre>
diff --git a/lib/stdlib/doc/src/uri_string_usage.xml b/lib/stdlib/doc/src/uri_string_usage.xml
new file mode 100644
index 0000000000..72851096b7
--- /dev/null
+++ b/lib/stdlib/doc/src/uri_string_usage.xml
@@ -0,0 +1,370 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2020</year>
+ <year>2020</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>Uniform Resource Identifiers</title>
+ <prepared>Péter Dimitrov</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2020-09-30</date>
+ <rev>PA1</rev>
+ <file>uri_string_usage.xml</file>
+ </header>
+ <section>
+ <title>Basics</title>
+ <p>At the time of writing this document, in October 2020, there are
+ two major standards concerning Universal Resource Identifiers and
+ Universal Resource Locators:</p>
+ <list type="bulleted">
+ <item><p>
+ <url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986 - Uniform Resource
+ Identifier (URI): Generic Syntax</url></p></item>
+ <item><p>
+ <url href="https://url.spec.whatwg.org/">WHAT WG URL - Living standard</url>
+ </p></item>
+ </list>
+ <p>
+ The former is a classical standard with a proper formal syntax, using the so
+ called <url href="https://www.ietf.org/rfc/rfc2234.txt">Augmented Backus-Naur Form
+ (ABNF)</url> for describing
+ the grammar, while the latter is a living document describing the current pratice,
+ that is, how a majority of Web browsers work with URIs. WHAT WG URL is Web focused
+ and it has no formal grammar but a plain english description of the algorithms
+ that should be followed.</p>
+ <p>What is the difference between them, if any? They provide an overlapping
+ definition for resource identifiers and they are not compatible.
+ The <seeerl marker="stdlib:uri_string"><c>uri_string</c></seeerl> module implements
+ <url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url> and the term URI will
+ be used throughout this document. A URI is an identifier, a string of characters
+ that identifies a particular resource.</p>
+ <p>
+ For a more complete problem
+ statement regarding the URIs check the
+ <url href="https://tools.ietf.org/html/draft-ruby-url-problem-01">URL Problem
+ Statement and Directions</url>.</p>
+ </section>
+
+ <section>
+ <title>What is a URI?</title>
+ <p>Let's start with what it is not. It is not the text that you type in the address
+ bar in your Web browser. Web browsers do all possible heuristics to convert the
+ input into a valid URI that could be sent over the network.</p>
+ <p>A URI is an identifier consisting of a sequence of characters matching the syntax
+ rule named <c>URI</c> in
+ <url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>.
+ </p>
+ <p>It is crucial to clarify that a <i>character</i> is a symbol that is displayed on
+ a terminal or written to paper and should not be confused with its internal
+ representation.</p>
+ <p>A URI more specifically, is a sequence of characters from a
+ subset of the US ASCII character set. The generic URI syntax consists of a
+ hierarchical sequence of components referred to as the scheme, authority,
+ path, query, and fragment. There is a formal description for
+ each of these components in
+ <url href="https://www.ietf.org/rfc/rfc2234.txt">ABNF</url> notation in
+ <url href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>:</p>
+ <pre>
+ URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+ hier-part = "//" authority path-abempty
+ / path-absolute
+ / path-rootless
+ / path-empty
+ scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+ authority = [ userinfo "@" ] host [ ":" port ]
+ userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+
+ reserved = gen-delims / sub-delims
+ gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+ sub-delims = "!" / "$" / "&amp;" / "'" / "(" / ")"
+ / "*" / "+" / "," / ";" / "="
+
+ unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ </pre>
+ </section>
+
+ <section>
+ <title>The uri_string module</title>
+ <p>As producing and consuming standard URIs can get quite complex, Erlang/OTP
+ provides
+ a module, <seeerl marker="stdlib:uri_string"><c>uri_string</c></seeerl>, to handle all the most difficult operations such as parsing,
+ recomposing, normalizing and resolving URIs against a base URI.
+ </p>
+ <p>The API functions in <seeerl marker="stdlib:uri_string"><c>uri_string</c></seeerl>
+ work on two basic data types
+ <seetype marker="uri_string#uri_string"><c>uri_string()</c></seetype> and
+ <seetype marker="uri_string#uri_map"><c>uri_map()</c></seetype>.
+ <seetype marker="uri_string#uri_string"><c>uri_string()</c></seetype> represents a
+ standard URI, while
+ <seetype marker="uri_string#uri_map"><c>uri_map()</c></seetype> is a wider datatype,
+ that can represent URI components using
+ <seeguide marker="unicode_usage#what-unicode-is">Unicode</seeguide> characters.
+ <seetype marker="uri_string#uri_map"><c>uri_map()</c></seetype>
+ is a convenient choice for enabling
+ operations such as producing standard compliant URIs out of components that have
+ special or <seeguide marker="unicode_usage#what-unicode-is">Unicode</seeguide>
+ characters. It is easier to explain this by an example.
+ </p>
+ <p>Let's say that we would like to create the following URI and send it over the
+ network: <c>http://cities/örebro?foo bar</c>. This is not a valid URI as it contains
+ characters that are not allowed in a URI such as "ö" and the space. We can verify
+ this by parsing the URI:
+ </p>
+ <pre>
+ 1> uri_string:parse("http://cities/örebro?foo bar").
+ {error,invalid_uri,":"}
+ </pre>
+ <p>The URI parser tries all possible combinations to interpret the input and fails
+ at the last attempt when it encounters the colon character <c>":"</c>. Note, that
+ the inital fault occurs when the parser attempts to interpret the character
+ <c>"ö"</c> and after a failure back-tracks to the point where it has another
+ possible parsing alternative.</p>
+ <p>The proper way to solve this problem is to use
+ <seemfa marker="uri_string#recompose/1"><c>uri_string:recompose/1</c></seemfa>
+ with a <seetype marker="uri_string#uri_map"><c>uri_map()</c></seetype> as input:</p>
+ <pre>
+ 2> uri_string:recompose(#{scheme => "http", host => "cities", path => "/örebro",
+ query => "foo bar"}).
+ "http://cities/%C3%B6rebro?foo%20bar"
+ </pre>
+ <p>The result is a valid URI where all the special characters are encoded as defined
+ by the standard. Applying
+ <seemfa marker="uri_string#parse/1"><c>uri_string:parse/1</c></seemfa> and
+ <seemfa marker="uri_string#percent_decode/1"><c>uri_string:percent_decode/1</c></seemfa>
+ on the URI returns the original input:
+ </p>
+ <pre>
+ 3> uri_string:percent_decode(uri_string:parse("http://cities/%C3%B6rebro?foo%20bar")).
+ #{host => "cities",path => "/örebro",query => "foo bar",
+ scheme => "http"}
+ </pre>
+ <p>This symmetric property is heavily used in our property test suite.
+ </p>
+ </section>
+
+ <section>
+ <title>Percent-encoding</title>
+ <p>As you have seen in the previous chapter, a standard URI can only contain a strict
+ subset of the US ASCII character set, moreover the allowed set of characters is not
+ the same in the different URI components. Percent-encoding is a mechanism to
+ represent a data octet in a component when that octet's corresponding character
+ is outside of
+ the allowed set or is being used as a delimiter. This is what you see when <c>"ö"</c>
+ is encoded as <c>%C3%B6</c> and <c>space</c> as <c>%20</c>.
+ Most of the API functions are
+ expecting UTF-8 encoding when handling percent-encoded triplets. The UTF-8 encoding
+ of the <seeguide marker="unicode_usage#what-unicode-is">Unicode</seeguide>
+ character <c>"ö"</c> is two octets: <c>OxC3 0xB6</c>.
+ The character <c>space</c> is in the first 128 characters of
+ <seeguide marker="unicode_usage#what-unicode-is">Unicode</seeguide> and it is encoded
+ using a single octet <c>0x20</c>.</p>
+ <note><p><seeguide marker="unicode_usage#what-unicode-is">Unicode</seeguide>
+ is backward compatible with ASCII, the encoding of the first 128
+ characters is the same binary value as in ASCII.
+ </p></note>
+ <p><marker id="percent_encoding"></marker>
+ It is a major source of confusion exactly which characters will be
+ percent-encoded. In order to make it easier to answer this question the library
+ provides a utility function,
+ <seemfa marker="uri_string#allowed_characters/0"><c>uri_string:allowed_characters/0
+ </c></seemfa>,
+ that lists the allowed set of characters in each major
+ URI component, and also in the most important standard character sets.
+ </p>
+ <pre>
+ 1> uri_string:allowed_characters().
+ <![CDATA[{scheme,
+ "+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"},
+ {userinfo,
+ "!$%&'()*+,-.0123456789:;=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"},
+ {host,
+ "!$&'()*+,-.0123456789:;=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"},
+ {ipv4,".0123456789"},
+ {ipv6,".0123456789:ABCDEFabcdef"},
+ {regname,
+ "!$%&'()*+,-.0123456789;=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"},
+ {path,
+ "!$%&'()*+,-./0123456789:;=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"},
+ {query,
+ "!$%&'()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"},
+ {fragment,
+ "!$%&'()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"},
+ {reserved,"!#$&'()*+,/:;=?@[]"},
+ {unreserved,
+ "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"}] ]]>
+ </pre>
+ <p>If a URI component has a character that is not allowed, it will be
+ percent-encoded when the URI is produced:
+ </p>
+ <pre>
+ 2> uri_string:recompose(#{scheme => "https", host => "local#host", path => ""}).
+ "https://local%23host"
+ </pre>
+ <p>Consuming a URI containing percent-encoded triplets can take many steps. The
+ following example shows how to handle an input URI that is not normalized and
+ contains multiple percent-encoded triplets.
+ First, the input <seetype marker="uri_string#uri_string"><c>uri_string()</c></seetype>
+ is to be parsed into a <seetype marker="uri_string#uri_map"><c>uri_map()</c></seetype>.
+ The parsing only splits the URI into its components without doing any decoding:
+ </p>
+ <pre>
+ 3> uri_string:parse("http://%6C%6Fcal%23host/%F6re%26bro%20").
+ #{host => "%6C%6Fcal%23host",path => "/%F6re%26bro%20",
+ scheme => "http"}}
+ </pre>
+ <p>The input is a valid URI but how can you decode those
+ percent-encoded octets? You can try to normalize the input with
+ <seemfa marker="uri_string#normalize/1"><c>uri_string:normalize/1</c></seemfa>. The
+ normalize operation decodes those
+ percent-encoded triplets that correspond to a character in the unreserved set.
+ Normalization is a safe, idempotent operation that converts a URI into its
+ canonical form:</p>
+ <pre>
+ 4> uri_string:normalize("http://%6C%6Fcal%23host/%F6re%26bro%20").
+ "http://local%23host/%F6re%26bro%20"
+ 5> uri_string:normalize("http://%6C%6Fcal%23host/%F6re%26bro%20", [return_map]).
+ #{host => "local%23host",path => "/%F6re%26bro%20",
+ scheme => "http"}
+ </pre>
+ <p>There are still a few percent-encoded triplets left in the output. At this point,
+ when the URI is already parsed, it is safe to apply application specific decoding on
+ the remaining character triplets. Erlang/OTP provides a function,
+ <seemfa marker="uri_string#percent_decode/1"><c>uri_string:percent_decode/1</c></seemfa>
+ for raw percent decoding
+ that you can use on the host and path components, or on the whole map:
+ </p>
+ <pre>
+ 6> uri_string:percent_decode("local%23host").
+ "local#host"
+ 7> uri_string:percent_decode("/%F6re%26bro%20").
+ <![CDATA[{error,invalid_utf8,<<"/öre&bro ">>}]]>
+ 8> uri_string:percent_decode(#{host => "local%23host",path => "/%F6re%26bro%20",
+ scheme => "http"}).
+ <![CDATA[{error,{invalid,{path,{invalid_utf8,<<"/öre&bro ">>}}}}]]>
+ </pre>
+ <p>The <c>host</c> was successfully decoded but the path contains at least one
+ character with
+ non-UTF-8 encoding. In order to be able to decode this, you have to make assumptions
+ about the encoding used in these triplets. The most obvious choice is
+ <i>latin-1</i>, so you can try
+ <seemfa marker="uri_string#transcode/2"><c>uri_string:transcode/2</c></seemfa>, to
+ transcode the path to UTF-8 and run the percent-decode operation on the
+ transcoded string:
+ </p>
+ <pre>
+ 9> uri_string:transcode("/%F6re%26bro%20", [{in_encoding, latin1}]).
+ "/%C3%B6re%26bro%20"
+ 10> uri_string:percent_decode("/%C3%B6re%26bro%20").
+ <![CDATA["/öre&bro "]]>
+ </pre>
+ <p>It is important to emphasize that it is not safe to apply
+ <seemfa marker="uri_string#percent_decode/1"><c>uri_string:percent_decode/1</c></seemfa>
+ directly on an input URI:
+ </p>
+ <pre>
+ 11> uri_string:percent_decode("http://%6C%6Fcal%23host/%C3%B6re%26bro%20").
+ <![CDATA["http://local#host/öre&bro "
+ 12> uri_string:parse("http://local#host/öre&bro ").]]>
+ {error,invalid_uri,":"}
+ </pre>
+ <note><p>Percent-encoding is implemented in
+ <seemfa marker="uri_string#recompose/1"><c>uri_string:recompose/1</c></seemfa>
+ and it happens when converting a
+ <seetype marker="uri_string#uri_map"><c>uri_map()</c></seetype>
+ into a <seetype marker="uri_string#uri_string"><c>uri_string()</c></seetype>.
+ There is no equivalent to a raw percent-encoding function as percent-encoding
+ shall be applied on the component level using different sets of allowed characters.
+ Applying percent-encoding directly on an input URI would not be safe just as in
+ the case of
+ <seemfa marker="uri_string#percent_decode/1"><c>uri_string:percent_decode/1</c></seemfa>,
+ the output could be an invalid URI.
+ </p>
+ </note>
+ </section>
+
+ <section>
+ <title>Normalization</title>
+ <p>Normalization is the operation of converting the input URI into a <i>canonical</i>
+ form and keeping the reference to the same underlying resource. The most common
+ application of normalization is determining whether two URIs are equivalent
+ without accessing their referenced resources.</p>
+ <p>Normalization has 6 distinct steps. First the input URI is parsed into an
+ intermediate form that can handle
+ <seeguide marker="unicode_usage#what-unicode-is">Unicode</seeguide> characters.
+ This datatype is the
+ <seetype marker="uri_string#uri_map"><c>uri_map()</c></seetype>, that can hold the
+ components of the URI in map elements of type
+ <seetype marker="unicode#chardata"><c>unicode:chardata()</c></seetype>.
+ After having the intermediate form, a sequence of
+ normalization algorithms are applied to the individual URI components:</p>
+ <taglist>
+ <tag>Case normalization</tag>
+ <item>
+ <p>Converts the <c>scheme</c> and <c>host</c> components
+ to lower case as they are not case sensitive.</p>
+ </item>
+ <tag>Percent-encoding normalization</tag>
+ <item>
+ <p>Decodes percent-encoded triplets that
+ correspond to characters in the unreserved set.</p>
+ </item>
+ <tag>Scheme-based normalization</tag>
+ <item>
+ <p>Applying rules for the schemes http, https,
+ ftp, ssh, sftp and tftp.</p>
+ </item>
+ <tag>Path segment normalization</tag>
+ <item>
+ <p>Converts the path into a canonical form.</p>
+ </item>
+ </taglist>
+ <p>After these steps, the intermediate data structure, an
+ <seetype marker="uri_string#uri_map"><c>uri_map()</c></seetype>,
+ is fully normalized. The last step is applying
+ <seemfa marker="uri_string#recompose/1"><c>uri_string:recompose/1</c></seemfa>
+ that converts the intermediate structure into a valid canonical URI string.</p>
+ <p>Notice the order, the
+ <seemfa marker="uri_string#normalize/2"><c>uri_string:normalize(URIMap, [return_map])</c></seemfa> that we
+ used many times in this user guide is a shortcut in the normalization process
+ returning the intermediate datastructure, and allowing us to inspect and apply
+ further decoding on the remaining percent-encoded triplets.</p>
+ <pre>
+ 13> uri_string:normalize("hTTp://LocalHost:80/%c3%B6rebro/a/../b").
+ "http://localhost/%C3%B6rebro/b"
+ 14> uri_string:normalize("hTTp://LocalHost:80/%c3%B6rebro/a/../b", [return_map]).
+ #{host => "localhost",path => "/%C3%B6rebro/b",
+ scheme => "http"}
+ </pre>
+ </section>
+
+ <section>
+ <title>Special considerations</title>
+ <p>The current URI implementation provides support for producing and consuming
+ standard URIs. The API is not meant to be directly exposed in a Web
+ browser's address bar where users can basically enter free text. Application
+ designers shall implement proper heuristics to map the input into a parsable URI.</p>
+ </section>
+
+</chapter>
diff --git a/lib/stdlib/doc/src/win32reg.xml b/lib/stdlib/doc/src/win32reg.xml
index 76d6bfdea3..3c34f66e38 100644
--- a/lib/stdlib/doc/src/win32reg.xml
+++ b/lib/stdlib/doc/src/win32reg.xml
@@ -87,7 +87,7 @@ hkdd HKEY_DYN_DATA</pre>
<p>Some registry values are stored as strings with references to environment
variables, for example, <c>%SystemRoot%Windows</c>. <c>SystemRoot</c> is
an environment variable, and is to be replaced with its value. Function
- <seealso marker="#expand/1"><c>expand/1</c></seealso> is provided so that
+ <seemfa marker="#expand/1"><c>expand/1</c></seemfa> is provided so that
environment variables surrounded by <c>%</c> can be expanded to their
values.</p>
<p>For more information on the Windows registry, see consult the Win32
@@ -99,7 +99,7 @@ hkdd HKEY_DYN_DATA</pre>
<name name="reg_handle"/>
<desc>
<p>As returned by
- <seealso marker="#open/1"><c>open/1</c></seealso>.</p>
+ <seemfa marker="#open/1"><c>open/1</c></seemfa>.</p>
</desc>
</datatype>
<datatype>
@@ -159,8 +159,8 @@ hkdd HKEY_DYN_DATA</pre>
<p>Deletes the current key, if it is valid. Calls the Win32 API
function <c>RegDeleteKey()</c>. Notice that this call does not change
the current key (unlike
- <seealso marker="#change_key_create/2">
- <c>change_key_create/2</c></seealso>).
+ <seemfa marker="#change_key_create/2">
+ <c>change_key_create/2</c></seemfa>).
This means that after the call, the current key is invalid.</p>
</desc>
</func>
@@ -204,9 +204,9 @@ hkdd HKEY_DYN_DATA</pre>
<p>Opens the registry for reading or writing. The current key is the
root (<c>HKEY_CLASSES_ROOT</c>). Flag <c>read</c> in the mode list
can be omitted.</p>
- <p>Use <seealso marker="#change_key/2"><c>change_key/2</c></seealso>
+ <p>Use <seemfa marker="#change_key/2"><c>change_key/2</c></seemfa>
with an absolute path after
- <seealso marker="#open/1"><c>open</c></seealso>.</p>
+ <seemfa marker="#open/1"><c>open</c></seemfa>.</p>
</desc>
</func>
@@ -256,7 +256,7 @@ hkdd HKEY_DYN_DATA</pre>
<desc>
<p>Retrieves a list of all values on the current key. The values
have types corresponding to the registry types, see
- <seealso marker="#value/2"><c>value/2</c></seealso>.
+ <seemfa marker="#value/2"><c>value/2</c></seemfa>.
Calls the Win32 API function <c>EnumRegValuesEx()</c>.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index 306ad27997..a056621ca1 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -47,42 +47,42 @@
<list type="bulleted">
<item>
<p>To create zip archives, use function
- <seealso marker="#zip/2"><c>zip/2</c></seealso> or
- <seealso marker="#zip/2"><c>zip/3</c></seealso>. They are
+ <seemfa marker="#zip/2"><c>zip/2</c></seemfa> or
+ <seemfa marker="#zip/2"><c>zip/3</c></seemfa>. They are
also available as <c>create/2,3</c>, to resemble the
- <seealso marker="erl_tar"><c>erl_tar</c></seealso> module.</p>
+ <seeerl marker="erl_tar"><c>erl_tar</c></seeerl> module.</p>
</item>
<item>
<p>To extract files from a zip archive, use function
- <seealso marker="#unzip/1"><c>unzip/1</c></seealso> or
- <seealso marker="#unzip/2"><c>unzip/2</c></seealso>. They are
+ <seemfa marker="#unzip/1"><c>unzip/1</c></seemfa> or
+ <seemfa marker="#unzip/2"><c>unzip/2</c></seemfa>. They are
also available as <c>extract/1,2</c>, to resemble the
- <seealso marker="erl_tar"><c>erl_tar</c></seealso> module.</p>
+ <seeerl marker="erl_tar"><c>erl_tar</c></seeerl> module.</p>
</item>
<item>
<p>To fold a function over all files in a zip archive, use function
- <seealso marker="#foldl/3"><c>foldl/3</c></seealso>.</p>
+ <seemfa marker="#foldl/3"><c>foldl/3</c></seemfa>.</p>
</item>
<item>
<p>To return a list of the files in a zip archive, use function
- <seealso marker="#list_dir/1"><c>list_dir/1</c></seealso> or
- <seealso marker="#list_dir/2"><c>list_dir/2</c></seealso>. They are
+ <seemfa marker="#list_dir/1"><c>list_dir/1</c></seemfa> or
+ <seemfa marker="#list_dir/2"><c>list_dir/2</c></seemfa>. They are
also available as <c>table/1,2</c>, to resemble the
- <seealso marker="erl_tar"><c>erl_tar</c></seealso> module.</p>
+ <seeerl marker="erl_tar"><c>erl_tar</c></seeerl> module.</p>
</item>
<item>
<p>To print a list of files to the Erlang shell, use function
- <seealso marker="#t/1"><c>t/1</c></seealso> or
- <seealso marker="#tt/1"><c>tt/1</c></seealso>.</p>
+ <seemfa marker="#t/1"><c>t/1</c></seemfa> or
+ <seemfa marker="#tt/1"><c>tt/1</c></seemfa>.</p>
</item>
<item>
<p>Sometimes it is desirable to open a zip archive, and to
unzip files from it file by file, without having to reopen the
archive. This can be done by functions
- <seealso marker="#zip_open/1"><c>zip_open/1,2</c></seealso>,
- <seealso marker="#zip_get/1"><c>zip_get/1,2</c></seealso>,
- <seealso marker="#zip_list_dir/1"><c>zip_list_dir/1</c></seealso>, and
- <seealso marker="#zip_close/1"><c>zip_close/1</c></seealso>.</p>
+ <seemfa marker="#zip_open/1"><c>zip_open/1,2</c></seemfa>,
+ <seemfa marker="#zip_get/1"><c>zip_get/1,2</c></seemfa>,
+ <seemfa marker="#zip_list_dir/1"><c>zip_list_dir/1</c></seemfa>, and
+ <seemfa marker="#zip_close/1"><c>zip_close/1</c></seemfa>.</p>
</item>
</list>
</description>
@@ -136,8 +136,8 @@
<tag><c>info</c></tag>
<item>
<p>File information as in
- <seealso marker="kernel:file#read_file_info/1">
- <c>file:read_file_info/1</c></seealso>
+ <seemfa marker="kernel:file#read_file_info/1">
+ <c>file:read_file_info/1</c></seemfa>
in Kernel</p>
</item>
<tag><c>comment</c></tag>
@@ -165,15 +165,15 @@
<datatype>
<name name="create_option"/>
<desc>
- <p>These options are described in <seealso marker="#zip_options">
- <c>create/3</c></seealso>.</p>
+ <p>These options are described in <seeerl marker="#zip_options">
+ <c>create/3</c></seeerl>.</p>
</desc>
</datatype>
<datatype>
<name name="handle"/>
<desc>
<p>As returned by
- <seealso marker="#zip_open/2"><c>zip_open/2</c></seealso>.</p>
+ <seemfa marker="#zip_open/2"><c>zip_open/2</c></seemfa>.</p>
</desc>
</datatype>
</datatypes>
@@ -245,7 +245,7 @@
<p><c>list_dir/2</c> provides options.</p>
<p><c>table/1</c> and <c>table/2</c> are provided as synonyms
to resemble the
- <seealso marker="erl_tar"><c>erl_tar</c></seealso> module.</p>
+ <seeerl marker="erl_tar"><c>erl_tar</c></seeerl> module.</p>
<p>The result value is the tuple <c>{ok, List}</c>, where <c>List</c>
contains the zip archive comment as the first element.</p>
<p>One option is available:</p>
@@ -293,7 +293,7 @@
<p><c>unzip/2</c> provides options to extract some files, and more.</p>
<p><c>extract/1</c> and <c>extract/2</c> are provided as synonyms
to resemble module
- <seealso marker="erl_tar"><c>erl_tar</c></seealso>.</p>
+ <seeerl marker="erl_tar"><c>erl_tar</c></seeerl>.</p>
<p>If argument <c><anno>Archive</anno></c> is specified as a binary,
the contents of the binary is assumed to be a zip archive,
otherwise a filename.</p>
@@ -344,8 +344,8 @@
<p>Uses the specified directory as current directory. It is
prepended to filenames when extracting them from the
zip archive. (Acting like
- <seealso marker="kernel:file#set_cwd/1">
- <c>file:set_cwd/1</c></seealso> in Kernel,
+ <seemfa marker="kernel:file#set_cwd/1">
+ <c>file:set_cwd/1</c></seemfa> in Kernel,
but without changing the global <c>cwd</c> property.)</p>
</item>
</taglist>
@@ -358,12 +358,14 @@
<name name="create" arity="2" since=""/>
<name name="create" arity="3" since=""/>
<fsummary>Create a zip archive with options.</fsummary>
+ <type name="create_option"/>
+ <type name="extension_spec"/>
<desc>
<p>Creates a zip archive containing the files specified in
<c><anno>FileList</anno></c>.</p>
<p><c>create/2</c> and <c>create/3</c> are provided as synonyms
to resemble module
- <seealso marker="erl_tar"><c>erl_tar</c></seealso>.</p>
+ <seeerl marker="erl_tar"><c>erl_tar</c></seeerl>.</p>
<p><c><anno>FileList</anno></c> is a list of files, with paths relative
to the current directory, which are stored with this path in the
archive. Files can also be specified with data in binaries
@@ -408,7 +410,7 @@
<p>The output is not to a file, but instead as a tuple
<c>{<anno>FileName</anno>, binary()}</c>. The binary is a full zip
archive with header and can be extracted with, for example,
- <seealso marker="#unzip/2"><c>unzip/2</c></seealso>.</p>
+ <seemfa marker="#unzip/2"><c>unzip/2</c></seemfa>.</p>
</item>
<tag><c>{comment, <anno>Comment</anno>}</c></tag>
<item>
@@ -419,8 +421,8 @@
<p>Uses the specified directory as current work directory
(<c>cwd</c>). This is prepended to filenames when adding them,
although not in the zip archive (acting like
- <seealso marker="kernel:file#set_cwd/1">
- <c>file:set_cwd/1</c></seealso> in Kernel, but without
+ <seemfa marker="kernel:file#set_cwd/1">
+ <c>file:set_cwd/1</c></seemfa> in Kernel, but without
changing the global <c>cwd</c> property.).</p>
</item>
<tag><c>{compress, <anno>What</anno>}</c></tag>
@@ -485,7 +487,7 @@
<fsummary>Close an open archive.</fsummary>
<desc>
<p>Closes a zip archive, previously opened with
- <seealso marker="#zip_open/1"><c>zip_open/1,2</c></seealso>.
+ <seemfa marker="#zip_open/1"><c>zip_open/1,2</c></seemfa>.
All resources are closed, and the handle is not to be used after
closing.</p>
</desc>
@@ -499,7 +501,7 @@
<p>Extracts one or all files from an open archive.</p>
<p>The files are unzipped to memory or to file, depending on
the options specified to function
- <seealso marker="#zip_open/1"><c>zip_open/1,2</c></seealso>
+ <seemfa marker="#zip_open/1"><c>zip_open/1,2</c></seemfa>
when opening the archive.</p>
</desc>
</func>
@@ -521,9 +523,9 @@
<p>Opens a zip archive, and reads and saves its directory. This
means that later reading files from the archive is
faster than unzipping files one at a time with
- <seealso marker="#unzip/1"><c>unzip/1,2</c></seealso>.</p>
+ <seemfa marker="#unzip/1"><c>unzip/1,2</c></seemfa>.</p>
<p>The archive must be closed with
- <seealso marker="#zip_close/1"><c>zip_close/1</c></seealso>.</p>
+ <seemfa marker="#zip_close/1"><c>zip_close/1</c></seemfa>.</p>
<p>The <c><anno>ZipHandle</anno></c> is closed if the
process that originally opened the archive dies.</p>
</desc>
diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl
index eab2ec4164..b1562e0207 100644
--- a/lib/stdlib/examples/erl_id_trans.erl
+++ b/lib/stdlib/examples/erl_id_trans.erl
@@ -193,9 +193,6 @@ pattern({map_field_exact,Line,K,V}) ->
Ke = expr(K),
Ve = pattern(V),
{map_field_exact,Line,Ke,Ve};
-%%pattern({struct,Line,Tag,Ps0}) ->
-%% Ps1 = pattern_list(Ps0),
-%% {struct,Line,Tag,Ps1};
pattern({record,Line,Name,Pfs0}) ->
Pfs1 = pattern_fields(Pfs0),
{record,Line,Name,Pfs1};
@@ -433,9 +430,6 @@ expr({map_field_exact,Line,K,V}) ->
Ke = expr(K),
Ve = expr(V),
{map_field_exact,Line,Ke,Ve};
-%%expr({struct,Line,Tag,Es0}) ->
-%% Es1 = pattern_list(Es0),
-%% {struct,Line,Tag,Es1};
expr({record_index,Line,Name,Field0}) ->
Field1 = expr(Field0),
{record_index,Line,Name,Field1};
@@ -619,6 +613,8 @@ type({atom,Line,A}) ->
{atom,Line,A};
type({integer,Line,I}) ->
{integer,Line,I};
+type({char,Line,C}) ->
+ {char,Line,C};
type({op,Line,Op,T}) ->
T1 = type(T),
{op,Line,Op,T1};
diff --git a/lib/stdlib/include/erl_bits.hrl b/lib/stdlib/include/erl_bits.hrl
index 2a54587a17..7810527832 100644
--- a/lib/stdlib/include/erl_bits.hrl
+++ b/lib/stdlib/include/erl_bits.hrl
@@ -31,19 +31,3 @@
sign :: bt_sign() | 'undefined',
endian :: bt_endian() | 'undefined'
}).
-
--record(bitdefault, {
- integer, %% default type for integer
- float, %% default type for float
- binary %% default type for binary
- }).
-
-%%% (From config.hrl in the bitsyntax branch.)
--define(SYS_ENDIAN, big).
--define(SIZEOF_CHAR, 1).
--define(SIZEOF_DOUBLE, 8).
--define(SIZEOF_FLOAT, 4).
--define(SIZEOF_INT, 4).
--define(SIZEOF_LONG, 4).
--define(SIZEOF_LONG_LONG, 8).
--define(SIZEOF_SHORT, 2).
diff --git a/lib/stdlib/scripts/update_deprecations b/lib/stdlib/scripts/update_deprecations
new file mode 100755
index 0000000000..47723ca4bc
--- /dev/null
+++ b/lib/stdlib/scripts/update_deprecations
@@ -0,0 +1,359 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-mode(compile).
+-compile(warnings_as_errors).
+
+-import(lists, [foldl/3,sort/1]).
+
+-record(st,
+ {functions = [],
+ types = [],
+ deprecations = #{}}).
+
+main(["update",Top]) ->
+ St0 = summarize(Top),
+ St = check_deprecations(Top, St0),
+ emit(Top, St),
+ halt(0);
+main(["make_xml",Type,Top,Outfile]) ->
+ St = summarize(Top),
+ make_xml(Top, Type, Outfile, St#st.functions),
+ halt(0).
+
+ebin_directories(Top) ->
+ [filename:join(Top, "erts/preloaded/ebin")] ++
+ filelib:wildcard(filename:join(Top, "lib/*/ebin")).
+
+summarize(Top) ->
+ Directories = ebin_directories(Top),
+ foldl(fun summarize_directory/2, #st{}, Directories).
+
+summarize_directory(Dir, Acc) ->
+ Files = [filename:join(Dir, F) || F <- filelib:wildcard("*.beam", Dir)],
+ foldl(fun summarize_file/2, Acc, Files).
+
+summarize_file(File, Acc) ->
+ {ok, {Module, [Chunk]}} = beam_lib:chunks(File, [attributes]),
+ {attributes, Attributes} = Chunk,
+ summarize_attributes(Attributes, Module, Acc).
+
+summarize_attributes([{deprecated, Ds} | As], Module, Acc0) ->
+ Fs = sa_1(Ds, deprecated, Module, Acc0#st.functions),
+ Acc = Acc0#st{ functions = Fs },
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([{removed, Rs} | As], Module, Acc0) ->
+ Fs = sa_1(Rs, removed, Module, Acc0#st.functions),
+ Acc = Acc0#st{ functions = Fs },
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([{deprecated_type, Ds} | As], Module, Acc0) ->
+ Ts = sa_1(Ds, deprecated, Module, Acc0#st.types),
+ Acc = Acc0#st{ types = Ts },
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([{removed_type, Rs} | As], Module, Acc0) ->
+ Ts = sa_1(Rs, removed, Module, Acc0#st.types),
+ Acc = Acc0#st{ types = Ts },
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([_ | As], Module, Acc) ->
+ summarize_attributes(As, Module, Acc);
+summarize_attributes([], _Module, Acc) ->
+ Acc.
+
+sa_1([{F, A, Info} | As], Tag, Module, Acc0) ->
+ sa_1(As, Tag, Module, [{Tag, Module, F, A, Info} | Acc0]);
+sa_1([{F, A} | As], Tag, Module, Acc0) ->
+ sa_1(As, Tag, Module, [{Tag, Module, F, A, undefined} | Acc0]);
+sa_1([module | As], Tag, Module, Acc0) ->
+ sa_1(As, Tag, Module, [{Tag, Module, '_', '_', undefined} | Acc0]);
+sa_1([], _Tag, _Module, Acc) ->
+ Acc.
+
+%%
+
+emit(Top, #st{ functions = Fs0, types = Ts, deprecations = Depr }) ->
+ Fs = insert_removals(Fs0, Depr),
+ Name = filename:join(Top, "lib/stdlib/src/otp_internal.erl"),
+ Contents = ["%%\n"
+ "%% WARNING: DO NOT EDIT THIS FILE.\n"
+ "%%\n"
+ "%% This file was auto-generated from attributes in the source\n"
+ "%% code.\n"
+ "%%\n"
+ "%% To add a description to a deprecation or removal attribute,\n"
+ "%% write a string after the arity:\n"
+ "%%\n"
+ "%% -deprecated([{foo,1,\"use bar/1 instead\"}]).\n"
+ "%% -deprecated_type([{gadget,1,\"use widget/1 instead\"}]).\n"
+ "%% -removed([{hello,2,\"use there/2 instead\"}]).\n"
+ "%% -removed_type([{frobnitz,1,\"use grunka/1 instead\"}]).\n"
+ "%%\n"
+ "%% Descriptions cannot be given with the `f/1` shorthand, and\n"
+ "%% it will fall back to a generic description referring the\n"
+ "%% user to the documentation.\n"
+ "%%\n"
+ "%% Use `./otp_build update_deprecations` to update this file\n"
+ "%% after adding an attribute.\n"
+ "%%\n"
+ "-module(otp_internal).\n"
+ "-include(\"otp_internal.hrl\").\n"
+ "%%\n",
+ emit_function("obsolete", Fs),
+ emit_function("obsolete_type", Ts)],
+ ok = file:write_file(Name, Contents),
+ ok.
+
+emit_function(FuncName, Entries) ->
+ [io_lib:format("-dialyzer({no_match, ~ts/3}).\n", [FuncName]),
+ [emit_clause(FuncName, E) || E <- sort_clauses(Entries)],
+ io_lib:format("~ts(_,_,_) -> no.\n\n", [FuncName])].
+
+sort_clauses(Entries) ->
+ Tagged = [{clause_order(E), E} || E <- Entries],
+ [E || {_, E} <- sort(Tagged)].
+
+clause_order({_Tag, _Module, F, A, _Info}=Entry) ->
+ {clause_order(F, A), Entry};
+clause_order({_Tag, _Module, F, A, _Info, _Rel}) ->
+ {clause_order(F, A), {_Tag, _Module, F, A, _Info}}.
+
+%% Wildcard matches must be emitted *after* specific matches to avoid
+%% losing descriptions.
+clause_order(F, A) when F =/= '_', A =/= '_' -> 0;
+clause_order(F, '_') when F =/= '_' -> 1;
+clause_order('_', A) when A =/= '_' -> 2;
+clause_order('_', '_') -> 3.
+
+emit_clause(FuncName, {Tag, M, F, A, Info}) ->
+ io_lib:format("~ts(~ts, ~ts, ~ts) ->\n"
+ " {~p, ~p};\n",
+ [FuncName, match_string(M), match_string(F), match_string(A),
+ Tag, info_string(Info)]);
+emit_clause(FuncName, {Tag, M, F, A, Info, Rel}) ->
+ io_lib:format("~ts(~ts, ~ts, ~ts) ->\n"
+ " {~p, ~p, ~p};\n",
+ [FuncName, match_string(M), match_string(F), match_string(A),
+ Tag, info_string(Info), Rel]).
+
+%%
+
+info_string(undefined) ->
+ "see the documentation for details";
+info_string(next_version) ->
+ "will be removed in the next version. "
+ "See the documentation for details";
+info_string(next_major_release) ->
+ "will be removed in the next major release. "
+ "See the documentation for details";
+info_string(eventually) ->
+ "will be removed in a future release. "
+ "See the documentation for details";
+info_string(String) when is_list(String) ->
+ String.
+
+match_string('_') -> "_";
+match_string(Term) -> io_lib:format("~p", [Term]).
+
+%%
+
+insert_removals([{deprecated,M,F,A,Info}=Entry|T], Depr) ->
+ Key = {M,F,A},
+ case Depr of
+ #{Key := Ps} ->
+ case lists:keyfind(remove, 1, Ps) of
+ false ->
+ [Entry|insert_removals(T, Depr)];
+ {remove,Rel0} ->
+ Rel = lists:concat(["OTP ",Rel0]),
+ [{deprecated,M,F,A,Info,Rel}|insert_removals(T, Depr)]
+ end;
+ #{} ->
+ [Entry|insert_removals(T, Depr)]
+ end;
+insert_removals([H|T], Depr) ->
+ [H|insert_removals(T, Depr)];
+insert_removals([], _Depr) ->
+ [].
+
+%%%
+%%% Create XML files.
+%%%
+
+make_xml(Top, Type, OutFile, InfoText0) ->
+ DeprecationFile = deprecation_file(Top),
+ OutDir = filename:dirname(DeprecationFile),
+ InfoTextMap = maps:from_list(make_xml_info(InfoText0)),
+ Depr0 = read_deprecations(DeprecationFile),
+ Depr = maps:to_list(Depr0),
+ {Key,Prefix} = case Type of
+ "deprecations" ->
+ {since,"deprecations"};
+ "removals" ->
+ {remove,"scheduled_for_removal"}
+ end,
+ Collected = make_xml_collect(Depr, Key, InfoTextMap, []),
+ All = make_xml_gen(lists:reverse(Collected), Type, Prefix, OutDir),
+ file:write_file(OutFile, All),
+ ok.
+
+make_xml_info([{deprecated,M,F,A,Text}|T]) ->
+ [{{M,F,A},Text}|make_xml_info(T)];
+make_xml_info([{removed,_,_,_,_}|T]) ->
+ make_xml_info(T);
+make_xml_info([]) ->
+ [].
+
+make_xml_collect([{MFA,Ps}|T], Key, InfoTextMap, Acc) ->
+ case lists:keyfind(Key, 1, Ps) of
+ {Key,Rel} ->
+ InfoText = case InfoTextMap of
+ #{MFA := Text} -> Text;
+ #{} -> []
+ end,
+ make_xml_collect(T, Key, InfoTextMap, [{Rel,{MFA,InfoText}}|Acc]);
+ false ->
+ make_xml_collect(T, Key, InfoTextMap, Acc)
+ end;
+make_xml_collect([], _Key, _InfoTextMap, Acc) ->
+ rel2fam(Acc).
+
+make_xml_gen(Collected, Type, Prefix, Dir) ->
+ Head = get_xml_template(Dir, Prefix, head),
+ Contents = make_xml_gen_list(Collected, Type, Prefix, Dir),
+ Footer = "</chapter>\n",
+ [Head,Contents,Footer].
+
+make_xml_gen_list([{Rel,MFAs}|T], Type, Prefix, Dir) ->
+ RelStr = lists:concat(["OTP ",Rel]),
+ RelMarker = lists:concat(["otp-",Rel]),
+ Head = ["<section>\n",
+ "<marker id=\"",RelMarker,"\"/>\n",
+ "<title>",RelStr,"</title>\n"],
+ Footer = "</section>\n",
+ SubTitle = case Type of
+ "deprecations" ->
+ ["Functions Deprecated in ",RelStr];
+ "removals" ->
+ ["Functions Scheduled for Removal in ",RelStr]
+ end,
+ SubHead = ["<section>\n",
+ "<title>",SubTitle,"</title>\n"],
+ SubFooter = "</section>\n",
+ [Head, get_xml_template(Dir, Prefix, Rel),
+ SubHead, make_xml_gen_mfas(MFAs), SubFooter,
+ Footer | make_xml_gen_list(T, Type, Prefix, Dir)];
+make_xml_gen_list([], _, _, _) ->
+ [].
+
+make_xml_gen_mfas(MFAs) ->
+ ["<list type=\"bulleted\">\n",
+ [make_xml_item(MFA) || MFA <- MFAs],
+ "</list>\n"].
+
+make_xml_item({{M,F,A},Text}) ->
+ ["<item><c>",lists:concat([M,":",F,"/",A]),"</c>",
+ " (",Text,")</item>\n"].
+
+get_xml_template(Dir, Prefix, Key) ->
+ Name = lists:concat([Prefix,"_",Key,".inc"]),
+ File = filename:join(Dir, Name),
+ case file:read_file(File) of
+ {ok,Contents} ->
+ Contents;
+ {error,enoent} ->
+ []
+ end.
+
+%%%
+%%% Cross-checks deprecations against DEPRECATIONS file.
+%%%
+
+check_deprecations(Top, #st{functions = Fs} = St) ->
+ DeprFile = deprecation_file(Top),
+ Depr = read_deprecations(DeprFile),
+ Bad0 = [F || F <- Fs, not in_deprecations(F, Depr)],
+ case Bad0 of
+ [] ->
+ St#st{deprecations = Depr};
+ [_|_] ->
+ Msg = "The following function(s) have -deprecated() attributes, "
+ "but are not present in the DEPRECATIONS file:\n\n",
+ Bad = [io_lib:format(" ~w:~w/~w\n", [M,F,A]) ||
+ {deprecated,M,F,A,_} <- Bad0],
+ Loc = ["\n","Please update ",DeprFile,".\n"],
+ io:put_chars(standard_error, [Msg,Bad,Loc]),
+ halt(1)
+ end.
+
+read_deprecations(File) ->
+ {ok,Bin} = file:read_file(File),
+ Lines = binary:split(Bin, <<"\n">>, [global,trim_all]),
+ maps:from_list(parse_deprecations(Lines)).
+
+deprecation_file(Root) ->
+ filename:join(Root, "system/doc/general_info/DEPRECATIONS").
+
+in_deprecations({deprecated,M,F,A,_}, Depr) ->
+ is_map_key({M,F,A}, Depr);
+in_deprecations({removed,_,_,_,_}, _Depr) ->
+ true.
+
+parse_deprecations([<<"#",_/binary>>|Lines]) ->
+ parse_deprecations(Lines);
+parse_deprecations([Line|Lines]) ->
+ [parse_line(Line)|parse_deprecations(Lines)];
+parse_deprecations([]) ->
+ [].
+
+parse_line(Line) ->
+ [MFA0|Parts0] = binary:split(Line, <<" ">>, [global,trim_all]),
+ MFA = parse_mfa(MFA0),
+ Parts1 = [binary:split(Part, <<"=">>) || Part <- Parts0],
+ Parts = lists:sort([parse_part(Part) || Part <- Parts1]),
+ {MFA,Parts}.
+
+parse_part([<<"mfa">>,MFA]) ->
+ {mfa,parse_mfa(MFA)};
+parse_part([<<"since">>,Since]) ->
+ {since,parse_release(Since)};
+parse_part([<<"remove">>,Remove]) ->
+ {remove,parse_release(Remove)}.
+
+parse_release(Rel) ->
+ binary_to_integer(Rel).
+
+parse_mfa(MFA) ->
+ {match,[M0,F0,A0]} = re:run(MFA, <<"^(\\w+):(\\w+)/([\\d_]+)$">>,
+ [{capture,all_but_first,binary}]),
+ A = case A0 of
+ <<"_">> -> '_';
+ _ -> binary_to_integer(A0)
+ end,
+ {bin_to_atom(M0),bin_to_atom(F0),A}.
+
+bin_to_atom(Bin) ->
+ list_to_atom(binary_to_list(Bin)).
+
+rel2fam(S0) ->
+ S1 = sofs:relation(S0),
+ S = sofs:rel2fam(S1),
+ sofs:to_external(S).
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index 9d8a5f98f9..e3e0c9c03d 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -112,6 +112,7 @@ MODULES= \
sets \
shell \
shell_default \
+ shell_docs \
slave \
sofs \
string \
@@ -133,7 +134,7 @@ HRL_FILES= \
../include/qlc.hrl \
../include/zip.hrl
-INTERNAL_HRL_FILES= dets.hrl erl_tar.hrl
+INTERNAL_HRL_FILES= dets.hrl erl_tar.hrl otp_internal.hrl
ERL_FILES= $(MODULES:%=%.erl)
@@ -149,6 +150,12 @@ APPUP_FILE= stdlib.appup
APPUP_SRC= $(APPUP_FILE).src
APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+ifeq ($(TARGET),win32)
+ EXE_SUFFIX=.exe
+else
+ EXE_SUFFIX=
+endif
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -207,7 +214,7 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
$(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
unicode_util.erl: ../uc_spec/*
- escript ../uc_spec/gen_unicode_mod.escript
+ escript$(EXE_SUFFIX) ../uc_spec/gen_unicode_mod.escript
# ----------------------------------------------------
# Release Target
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index 48d18ec5b9..23413f43b1 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -107,6 +107,7 @@
-type chnk_rsn() :: {'unknown_chunk', file:filename(), atom()}
| {'key_missing_or_invalid', file:filename(),
'abstract_code' | 'debug_info'}
+ | {'missing_backend', file:filename(), module()}
| info_rsn().
-type cmp_rsn() :: {'modules_different', module(), module()}
| {'chunks_different', chunkid()}
@@ -310,6 +311,9 @@ format_error(badfun) ->
"not a fun or the fun has the wrong arity";
format_error(exists) ->
"a fun has already been installed";
+format_error({missing_backend, File, Backend}) ->
+ io_lib:format("~tp: Cannot retrieve abstract code because the backend ~p is missing",
+ [File, Backend]);
format_error(E) ->
io_lib:format("~tp~n", [E]).
@@ -682,10 +686,13 @@ chunks_to_data([{abst_chunk, Name} | CNs], Chunks, File, Cs, Module, Atoms, L) -
{NewAtoms, Ret} =
case catch chunk_to_data(debug_info, DbgiChunk, File, Cs, Atoms, Module) of
{DbgiAtoms, {debug_info, {debug_info_v1, Backend, Metadata}}} ->
- case Backend:debug_info(erlang_v1, Module, Metadata, []) of
+ try Backend:debug_info(erlang_v1, Module, Metadata, []) of
{ok, Code} -> {DbgiAtoms, {abstract_code, {raw_abstract_v1, Code}}};
{error, _} -> {DbgiAtoms, {abstract_code, no_abstract_code}}
- end;
+ catch
+ error:undef ->
+ error({missing_backend,File,Backend})
+ end;
{error,beam_lib,{key_missing_or_invalid,Path,debug_info}} ->
error({key_missing_or_invalid,Path,abstract_code});
_ ->
@@ -973,7 +980,7 @@ decrypt_chunk(Type, Module, File, Id, Bin) ->
KeyString = get_crypto_key({debug_info, Type, Module, File}),
{Type,Key,IVec,_BlockSize} = make_crypto_key(Type, KeyString),
ok = start_crypto(),
- NewBin = crypto:block_decrypt(Type, Key, IVec, Bin),
+ NewBin = crypto:crypto_one_time(des_ede3_cbc, Key, IVec, Bin, false),
binary_to_term(NewBin)
catch
_:_ ->
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index 0362b72536..fa7bf8bfbc 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -19,6 +19,8 @@
%%
-module(c).
+-include_lib("kernel/include/eep48.hrl").
+
%% Utilities to use from shell.
%% Avoid warning for local function error/2 clashing with autoimported BIF.
@@ -28,6 +30,7 @@
lc_batch/0, lc_batch/1,
i/3,pid/3,m/0,m/1,mm/0,lm/0,
bt/1, q/0,
+ h/1,h/2,h/3,ht/1,ht/2,ht/3,hcb/1,hcb/2,hcb/3,
erlangrc/0,erlangrc/1,bi/1, flush/0, regs/0, uptime/0,
nregs/0,pwd/0,ls/0,ls/1,cd/1,memory/1,memory/0, xm/1]).
@@ -48,6 +51,9 @@ help() ->
"cd(Dir) -- change working directory\n"
"flush() -- flush any messages sent to the shell\n"
"help() -- help info\n"
+ "h(M) -- module documentation\n"
+ "h(M,F) -- module function documentation\n"
+ "h(M,F,A) -- module function arity documentation\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"
@@ -147,6 +153,122 @@ c(SrcFile, NewOpts, Filter, BeamFile, Info) ->
format("Recompiling ~ts\n", [SrcFile]),
safe_recompile(SrcFile, Options, BeamFile).
+-type h_return() :: ok | {error, missing | {unknown_format, unicode:chardata()}}.
+-type hf_return() :: h_return() | {error, function_missing}.
+-type ht_return() :: h_return() | {error, type_missing}.
+-type hcb_return() :: h_return() | {error, callback_missing}.
+
+-spec h(module()) -> h_return().
+h(Module) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render(Module, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec h(module(),function()) -> hf_return().
+h(Module,Function) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render(Module, Function, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec h(module(),function(),arity()) -> hf_return().
+h(Module,Function,Arity) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render(Module, Function, Arity, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec ht(module()) -> h_return().
+ht(Module) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render_type(Module, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec ht(module(),Type :: atom()) -> ht_return().
+ht(Module,Type) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render_type(Module, Type, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec ht(module(),Type :: atom(),arity()) ->
+ ht_return().
+ht(Module,Type,Arity) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render_type(Module, Type, Arity, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec hcb(module()) -> h_return().
+hcb(Module) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render_callback(Module, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec hcb(module(),Callback :: atom()) -> hcb_return().
+hcb(Module,Callback) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render_callback(Module, Callback, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+-spec hcb(module(),Callback :: atom(),arity()) ->
+ hcb_return().
+hcb(Module,Callback,Arity) ->
+ case code:get_doc(Module) of
+ {ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
+ format_docs(shell_docs:render_callback(Module, Callback, Arity, Docs));
+ {ok, #docs_v1{ format = Enc }} ->
+ {error, {unknown_format, Enc}};
+ Error ->
+ Error
+ end.
+
+format_docs({error,_} = E) ->
+ E;
+format_docs(Docs) ->
+ {match, Lines} = re:run(Docs,"(.+\n|\n)",[unicode,global,{capture,all_but_first,binary}]),
+ _ = paged_output(fun(Line,_) ->
+ format("~ts",Line),
+ {1,undefined}
+ end, undefined, Lines),
+ ok.
+
old_options(Info) ->
case lists:keyfind(options, 1, Info) of
{options, Opts} -> Opts;
@@ -475,58 +597,54 @@ 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"),
- iformat("Registered", "Current Function", "Stack", "",
- ""),
- {R,M,H,S} = foldl(fun(Pid, {R0,M0,H0,S0}) ->
- {A,B,C,D} = display_info(Pid),
- {R0+A,M0+B,H0+C,S0+D}
- end, {0,0,0,0}, Ps),
- iformat("Total", "", w(H), w(R), w(M)),
- iformat("", "", w(S), "", "");
-i(Ps, N) ->
- iformat("Pid", "Initial Call", "Heap", "Reds",
- "Msgs"),
- iformat("Registered", "Current Function", "Stack", "",
- ""),
- paged_i(Ps, {0,0,0,0}, N, 50).
-
-paged_i([], {R,M,H,S}, _, _) ->
- iformat("Total", "", w(H), w(R), w(M)),
- iformat("", "", w(S), "", "");
-paged_i(Ps, Acc, N, Page) ->
- {Pids, Rest, N1} =
- if N > Page ->
- {L1,L2} = lists:split(Page, Ps),
- {L1,L2,N-Page};
- true ->
- {Ps, [], 0}
- end,
- NewAcc = foldl(fun(Pid, {R,M,H,S}) ->
- {A,B,C,D} = display_info(Pid),
- {R+A,M+B,H+C,S+D}
- end, Acc, Pids),
- case Rest of
- [_|_] ->
- choice(fun() -> paged_i(Rest, NewAcc, N1, Page) end);
- [] ->
- paged_i([], NewAcc, 0, Page)
+ iformat("Pid", "Initial Call", "Heap", "Reds", "Msgs"),
+ iformat("Registered", "Current Function", "Stack", "", ""),
+ case paged_output(fun(Pid, {R,M,H,S}) ->
+ {A,B,C,D} = display_info(Pid),
+ {2,{R+A,M+B,H+C,S+D}}
+ end, 2, {0,0,0,0}, Ps) of
+ {R,M,H,S} ->
+ iformat("Total", "", w(H), w(R), w(M)),
+ iformat("", "", w(S), "", "");
+ less ->
+ ok
end.
-choice(F) ->
- case get_line('(c)ontinue (q)uit -->', "c\n") of
+paged_output(Fun, Acc, Items) ->
+ paged_output(Fun, 0, Acc, Items).
+paged_output(Fun, CurrLine, Acc, Items) ->
+ Limit =
+ case io:rows() of
+ {ok, Rows} -> Rows-2;
+ _ -> 100
+ end,
+ paged_output(Fun, CurrLine, Limit, Acc, Items).
+
+paged_output(PrintFun, CurrLine, Limit, Acc, Items) when CurrLine >= Limit ->
+ case more() of
+ more ->
+ paged_output(PrintFun, 0, Limit, Acc, Items);
+ less ->
+ less
+ end;
+paged_output(PrintFun, CurrLine, Limit, Acc, [H|T]) ->
+ {Lines, NewAcc} = PrintFun(H, Acc),
+ paged_output(PrintFun, CurrLine+Lines, Limit, NewAcc, T);
+paged_output(_, _, _, Acc, []) ->
+ Acc.
+
+more() ->
+ case get_line('more (y/n)? (y) ', "y\n") of
"c\n" ->
- F();
+ more;
+ "y\n" ->
+ more;
"q\n" ->
- quit;
+ less;
+ "n\n" ->
+ less;
_ ->
- choice(F)
+ more()
end.
get_line(P, Default) ->
diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl
index ef6d1882e6..2f95f54312 100644
--- a/lib/stdlib/src/calendar.erl
+++ b/lib/stdlib/src/calendar.erl
@@ -54,7 +54,8 @@
valid_date/1,
valid_date/3]).
--deprecated([{local_time_to_universal_time,1}]).
+-deprecated([{local_time_to_universal_time,1,
+ "use calendar:local_time_to_universal_time_dst/1 instead"}]).
-define(SECONDS_PER_MINUTE, 60).
-define(SECONDS_PER_HOUR, 3600).
diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl
index 8a4df95027..58d493cf54 100644
--- a/lib/stdlib/src/digraph.erl
+++ b/lib/stdlib/src/digraph.erl
@@ -230,7 +230,7 @@ in_neighbours(G, V) ->
Edges :: [edge()].
in_edges(G, V) ->
- ets:select(G#digraph.ntab, [{{{in, V}, '$1'}, [], ['$1']}]).
+ [E || {{in, _}, E} <- ets:lookup(G#digraph.ntab, {in, V})].
-spec out_degree(G, V) -> non_neg_integer() when
G :: graph(),
@@ -255,7 +255,7 @@ out_neighbours(G, V) ->
Edges :: [edge()].
out_edges(G, V) ->
- ets:select(G#digraph.ntab, [{{{out, V}, '$1'}, [], ['$1']}]).
+ [E || {{out, _}, E} <- ets:lookup(G#digraph.ntab, {out, V})].
-spec add_edge(G, V1, V2) -> edge() | {'error', add_edge_err_rsn()} when
G :: graph(),
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index f027d05f55..6078c5e67b 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -352,9 +352,6 @@ do_op({blink,C,M}, Bef=[$$,$$|_], Aft, Rs) ->
%% don't blink after a $
do_op({blink,C,_}, Bef=[$$|_], Aft, Rs) ->
do_op({insert,C}, Bef, Aft, Rs);
-%do_op({blink,C,M}, Bef, [], Rs) ->
-% N = over_paren(Bef, C, M),
-% {blink,N+1,{[C|Bef],[]},[{move_rel,-(N+1)},{put_chars,[C]}|Rs]};
do_op({blink,C,M}, Bef, Aft, Rs) ->
case over_paren(Bef, C, M) of
beep ->
diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl
index bdcefda6e5..bb6ad26d8f 100644
--- a/lib/stdlib/src/edlin_expand.erl
+++ b/lib/stdlib/src/edlin_expand.erl
@@ -32,35 +32,71 @@
%% function name must be on the same line. CurrentBefore is reversed
%% and over_word/3 reverses the characters it finds. In certain cases
%% possible expansions are printed.
+%%
+%% The function also handles expansion with "h(" for module and functions.
expand(Bef0) ->
{Bef1,Word,_} = edlin:over_word(Bef0, [], 0),
case over_white(Bef1, [], 0) of
- {[$:|Bef2],_White,_Nwh} ->
+ {[$,|Bef2],_White,_Nwh} ->
+ {Bef3,_White1,_Nwh1} = over_white(Bef2, [], 0),
+ {Bef4,Mod,_Nm} = edlin:over_word(Bef3, [], 0),
+ case expand_function(Bef4) of
+ help ->
+ expand_function_name(Mod, Word, ",");
+ _ ->
+ expand_module_name(Word, ",")
+ end;
+ {[$:|Bef2],_White,_Nwh} ->
{Bef3,_White1,_Nwh1} = over_white(Bef2, [], 0),
{_,Mod,_Nm} = edlin:over_word(Bef3, [], 0),
- expand_function_name(Mod, Word);
+ expand_function_name(Mod, Word, "(");
{_,_,_} ->
- expand_module_name(Word)
+ CompleteChar
+ = case expand_function(Bef1) of
+ help -> ",";
+ _ -> ":"
+ end,
+ expand_module_name(Word, CompleteChar)
end.
-expand_module_name(Prefix) ->
- match(Prefix, code:all_loaded(), ":").
+expand_function("("++Str) ->
+ case edlin:over_word(Str, [], 0) of
+ {_,"h",_} ->
+ help;
+ {_,"ht",_} ->
+ help_type;
+ _ ->
+ module
+ end;
+expand_function(_) ->
+ module.
+
+expand_module_name("",_) ->
+ {no, [], []};
+expand_module_name(Prefix,CompleteChar) ->
+ match(Prefix, [{list_to_atom(M),P} || {M,P,_} <- code:all_available()], CompleteChar).
-expand_function_name(ModStr, FuncPrefix) ->
+expand_function_name(ModStr, FuncPrefix, CompleteChar) ->
case to_atom(ModStr) of
{ok, Mod} ->
- case erlang:module_loaded(Mod) of
- true ->
- L = Mod:module_info(),
- case lists:keyfind(exports, 1, L) of
- {_, Exports} ->
- match(FuncPrefix, Exports, "(");
- _ ->
- {no, [], []}
- end;
- false ->
- {no, [], []}
- end;
+ Exports =
+ case erlang:module_loaded(Mod) of
+ true ->
+ Mod:module_info(exports);
+ false ->
+ case beam_lib:chunks(code:which(Mod), [exports]) of
+ {ok, {Mod, [{exports,E}]}} ->
+ E;
+ _ ->
+ {no, [], []}
+ end
+ end,
+ case Exports of
+ {no, [], []} ->
+ {no, [], []};
+ Exports ->
+ match(FuncPrefix, Exports, CompleteChar)
+ end;
error ->
{no, [], []}
end.
@@ -99,8 +135,10 @@ match(Prefix, Alts, Extra0) ->
{no, [], []}
end.
-flat_write(T) ->
- lists:flatten(io_lib:fwrite("~tw",[T])).
+flat_write(T) when is_atom(T) ->
+ lists:flatten(io_lib:fwrite("~tw",[T]));
+flat_write(S) ->
+ S.
%% Return the list of names L in multiple columns.
format_matches(L) ->
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 181a524db6..92b43a89ce 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1106,6 +1106,8 @@ scan_if([{'(',_}|_]=Toks, If, From, St) ->
Error = case Error0 of
{_,erl_parse,_} ->
{error,Error0};
+ {error,ErrL,What} ->
+ {error,{ErrL,epp,What}};
_ ->
{error,{loc(If),epp,Error0}}
end,
diff --git a/lib/stdlib/src/erl_error.erl b/lib/stdlib/src/erl_error.erl
index fdcb9e824c..5fbf5a6282 100644
--- a/lib/stdlib/src/erl_error.erl
+++ b/lib/stdlib/src/erl_error.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@
%%
-module(erl_error).
--export([format_exception/6, format_exception/7,
+-export([format_exception/6, format_exception/7, format_exception/8,
format_stacktrace/4, format_stacktrace/5,
format_call/4, format_call/5, format_fun/1, format_fun/2]).
@@ -38,20 +38,34 @@ format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun) ->
%% -> iolist() | unicode:charlist() (no \n at end)
%% FormatFun = fun(Term, I) -> iolist() | unicode:charlist().
-format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, Encoding)
+format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, Encoding) ->
+ FF = wrap_format_fun_2(FormatFun),
+ format_exception(I, Class, Reason, StackTrace, StackFun, FF, Encoding, -1).
+
+format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, Encoding,
+ CharsLimit)
when is_integer(I), I >= 1, is_function(StackFun, 3),
- is_function(FormatFun, 2) ->
+ is_function(FormatFun, 3), is_integer(CharsLimit) ->
S = n_spaces(I-1),
{Term,Trace1,Trace} = analyze_exception(Class, Reason, StackTrace),
- Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S, Encoding),
+ StLimit = if
+ CharsLimit < 0 ->
+ CharsLimit;
+ true ->
+ %% Reserve one third for the stacktrace.
+ CharsLimit div 3
+ end,
+ St = format_stacktrace1(S, Trace, FormatFun, StackFun, Encoding, StLimit),
+ Lim = sub(sub(CharsLimit, exited(Class), latin1), St, Encoding),
+ Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S, Encoding, Lim),
FormatString = case Encoding of
latin1 -> "~s~s";
_ -> "~s~ts"
end,
Expl = io_lib:fwrite(FormatString, [exited(Class), Expl0]),
- case format_stacktrace1(S, Trace, FormatFun, StackFun, Encoding) of
+ case St of
[] -> Expl;
- Stack -> [Expl, $\n, Stack]
+ _ -> [Expl, $\n, St]
end.
%% -> iolist() (no \n at end)
@@ -63,7 +77,8 @@ format_stacktrace(I, StackTrace, StackFun, FormatFun, Encoding)
when is_integer(I), I >= 1, is_function(StackFun, 3),
is_function(FormatFun, 2) ->
S = n_spaces(I-1),
- format_stacktrace1(S, StackTrace, FormatFun, StackFun, Encoding).
+ FF = wrap_format_fun_2(FormatFun),
+ format_stacktrace1(S, StackTrace, FF, StackFun, Encoding, -1).
%% -> iolist() (no \n at end)
format_call(I, ForMForFun, As, FormatFun) ->
@@ -72,7 +87,8 @@ format_call(I, ForMForFun, As, FormatFun) ->
%% -> iolist() | unicode:charlist() (no \n at end)
format_call(I, ForMForFun, As, FormatFun, Enc)
when is_integer(I), I >= 1, is_list(As), is_function(FormatFun, 2) ->
- format_call("", n_spaces(I-1), ForMForFun, As, FormatFun, Enc).
+ FF = wrap_format_fun_2(FormatFun),
+ format_call("", n_spaces(I-1), ForMForFun, As, FF, Enc).
%% -> iolist() (no \n at end)
format_fun(Fun) ->
@@ -94,6 +110,9 @@ format_fun(Fun, Enc) when is_function(Fun) ->
mfa_to_string(M, F, A, Enc)
end.
+wrap_format_fun_2(FormatFun) ->
+ fun(T, I1, CL) -> {FormatFun(T, I1), CL} end.
+
analyze_exception(error, Term, Stack) ->
case {is_stacktrace(Stack), Stack, Term} of
{true, [{_,_,As,_}=MFAL|MFAs], function_clause} when is_list(As) ->
@@ -127,82 +146,83 @@ is_stacktrace(_) ->
false.
%% ERTS exit codes (some of them are also returned by erl_eval):
-explain_reason(badarg, error, [], _PF, _S, _Enc) ->
+explain_reason(badarg, error, [], _PF, _S, _Enc, _CL) ->
<<"bad argument">>;
-explain_reason({badarg,V}, error=Cl, [], PF, S, _Enc) -> % orelse, andalso
- format_value(V, <<"bad argument: ">>, Cl, PF, S);
-explain_reason(badarith, error, [], _PF, _S, _Enc) ->
+explain_reason({badarg,V}, error=Cl, [], PF, S, _Enc, CL) -> % orelse, andalso
+ format_value(V, <<"bad argument: ">>, Cl, PF, S, CL);
+explain_reason(badarith, error, [], _PF, _S, _Enc, _CL) ->
<<"an error occurred when evaluating an arithmetic expression">>;
-explain_reason({badarity,{Fun,As}}, error, [], _PF, _S, Enc)
+explain_reason({badarity,{Fun,As}}, error, [], _PF, _S, Enc, _CL)
when is_function(Fun) ->
%% Only the arity is displayed, not the arguments As.
io_lib:fwrite(<<"~ts called with ~s">>,
[format_fun(Fun, Enc), argss(length(As))]);
-explain_reason({badfun,Term}, error=Cl, [], PF, S, _Enc) ->
- format_value(Term, <<"bad function ">>, Cl, PF, S);
-explain_reason({badmatch,Term}, error=Cl, [], PF, S, _Enc) ->
+explain_reason({badfun,Term}, error=Cl, [], PF, S, _Enc, CL) ->
+ format_value(Term, <<"bad function ">>, Cl, PF, S, CL);
+explain_reason({badmatch,Term}, error=Cl, [], PF, S, _Enc, CL) ->
Str = <<"no match of right hand side value ">>,
- format_value(Term, Str, Cl, PF, S);
-explain_reason({case_clause,V}, error=Cl, [], PF, S, _Enc) ->
+ format_value(Term, Str, Cl, PF, S, CL);
+explain_reason({case_clause,V}, error=Cl, [], PF, S, _Enc, CL) ->
%% "there is no case clause with a true guard sequence and a
%% pattern matching..."
- format_value(V, <<"no case clause matching ">>, Cl, PF, S);
-explain_reason(function_clause, error, [{F,A}], _PF, _S, _Enc) ->
+ format_value(V, <<"no case clause matching ">>, Cl, PF, S, CL);
+explain_reason(function_clause, error, [{F,A}], _PF, _S, _Enc, _CL) ->
%% Shell commands
FAs = io_lib:fwrite(<<"~w/~w">>, [F, A]),
[<<"no function clause matching call to ">> | FAs];
-explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S, Enc) ->
+explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S, Enc, CL) ->
Str = <<"no function clause matching ">>,
- [format_errstr_call(Str, Cl, {M,F}, As, PF, S, Enc),$\s|location(Loc)];
-explain_reason(if_clause, error, [], _PF, _S, _Enc) ->
+ [format_errstr_call(Str, Cl, {M,F}, As, PF, S, Enc, CL),$\s|location(Loc)];
+explain_reason(if_clause, error, [], _PF, _S, _Enc, _CL) ->
<<"no true branch found when evaluating an if expression">>;
-explain_reason(noproc, error, [], _PF, _S, _Enc) ->
+explain_reason(noproc, error, [], _PF, _S, _Enc, _CL) ->
<<"no such process or port">>;
-explain_reason(notalive, error, [], _PF, _S, _Enc) ->
+explain_reason(notalive, error, [], _PF, _S, _Enc, _CL) ->
<<"the node cannot be part of a distributed system">>;
-explain_reason(system_limit, error, [], _PF, _S, _Enc) ->
+explain_reason(system_limit, error, [], _PF, _S, _Enc, _CL) ->
<<"a system limit has been reached">>;
-explain_reason(timeout_value, error, [], _PF, _S, _Enc) ->
+explain_reason(timeout_value, error, [], _PF, _S, _Enc, _CL) ->
<<"bad receive timeout value">>;
-explain_reason({try_clause,V}, error=Cl, [], PF, S, _Enc) ->
+explain_reason({try_clause,V}, error=Cl, [], PF, S, _Enc, CL) ->
%% "there is no try clause with a true guard sequence and a
%% pattern matching..."
- format_value(V, <<"no try clause matching ">>, Cl, PF, S);
-explain_reason(undef, error, [{M,F,A,_}], _PF, _S, Enc) ->
+ format_value(V, <<"no try clause matching ">>, Cl, PF, S, CL);
+explain_reason(undef, error, [{M,F,A,_}], _PF, _S, Enc, _CL) ->
%% Only the arity is displayed, not the arguments, if there are any.
io_lib:fwrite(<<"undefined function ~ts">>,
[mfa_to_string(M, F, n_args(A), Enc)]);
-explain_reason({shell_undef,F,A,_}, error, [], _PF, _S, Enc) ->
+explain_reason({shell_undef,F,A,_}, error, [], _PF, _S, Enc, _CL) ->
%% Give nicer reports for undefined shell functions
%% (but not when the user actively calls shell_default:F(...)).
FS = to_string(F, Enc),
io_lib:fwrite(<<"undefined shell command ~ts/~w">>, [FS, n_args(A)]);
%% Exit codes returned by erl_eval only:
-explain_reason({argument_limit,_Fun}, error, [], _PF, _S, _Enc) ->
+explain_reason({argument_limit,_Fun}, error, [], _PF, _S, _Enc, _CL) ->
io_lib:fwrite(<<"limit of number of arguments to interpreted function"
" exceeded">>, []);
-explain_reason({bad_filter,V}, error=Cl, [], PF, S, _Enc) ->
- format_value(V, <<"bad filter ">>, Cl, PF, S);
-explain_reason({bad_generator,V}, error=Cl, [], PF, S, _Enc) ->
- format_value(V, <<"bad generator ">>, Cl, PF, S);
-explain_reason({unbound,V}, error, [], _PF, _S, _Enc) ->
+explain_reason({bad_filter,V}, error=Cl, [], PF, S, _Enc, CL) ->
+ format_value(V, <<"bad filter ">>, Cl, PF, S, CL);
+explain_reason({bad_generator,V}, error=Cl, [], PF, S, _Enc, CL) ->
+ format_value(V, <<"bad generator ">>, Cl, PF, S, CL);
+explain_reason({unbound,V}, error, [], _PF, _S, _Enc, _CL) ->
io_lib:fwrite(<<"variable ~w is unbound">>, [V]);
%% Exit codes local to the shell module (restricted shell):
-explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S, _Enc) ->
+explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S, _Enc, CL) ->
Str = <<"restricted shell module returned bad value ">>,
- format_value(V, Str, Cl, PF, S);
+ format_value(V, Str, Cl, PF, S, CL);
explain_reason({restricted_shell_disallowed,{ForMF,As}},
- exit=Cl, [], PF, S, Enc) ->
+ exit=Cl, [], PF, S, Enc, CL) ->
%% ForMF can be a fun, but not a shell fun.
Str = <<"restricted shell does not allow ">>,
- format_errstr_call(Str, Cl, ForMF, As, PF, S, Enc);
-explain_reason(restricted_shell_started, exit, [], _PF, _S, _Enc) ->
+ format_errstr_call(Str, Cl, ForMF, As, PF, S, Enc, CL);
+explain_reason(restricted_shell_started, exit, [], _PF, _S, _Enc, _CL) ->
<<"restricted shell starts now">>;
-explain_reason(restricted_shell_stopped, exit, [], _PF, _S, _Enc) ->
+explain_reason(restricted_shell_stopped, exit, [], _PF, _S, _Enc, _CL) ->
<<"restricted shell stopped">>;
%% Other exit code:
-explain_reason(Reason, Class, [], PF, S, _Enc) ->
- PF(Reason, (iolist_size(S)+1) + exited_size(Class)).
+explain_reason(Reason, Class, [], PF, S, _Enc, CL) ->
+ {L, _} = PF(Reason, (iolist_size(S)+1) + exited_size(Class), CL),
+ L.
n_args(A) when is_integer(A) ->
A;
@@ -218,29 +238,33 @@ argss(2) ->
argss(I) ->
io_lib:fwrite(<<"~w arguments">>, [I]).
-format_stacktrace1(S0, Stack0, PF, SF, Enc) ->
+format_stacktrace1(S0, Stack0, PF, SF, Enc, CL) ->
Stack1 = lists:dropwhile(fun({M,F,A,_}) -> SF(M, F, A)
end, lists:reverse(Stack0)),
S = [" " | S0],
Stack = lists:reverse(Stack1),
- format_stacktrace2(S, Stack, 1, PF, Enc).
-
-format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF, Enc) when is_integer(A) ->
- [io_lib:fwrite(<<"~s~s ~ts ~ts">>,
- [sep(N, S), origin(N, M, F, A),
- mfa_to_string(M, F, A, Enc),
- location(L)])
- | format_stacktrace2(S, Fs, N + 1, PF, Enc)];
-format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF, Enc) when is_list(As) ->
+ format_stacktrace2(S, Stack, 1, PF, Enc, CL).
+
+format_stacktrace2(_S, _Stack, _N, _PF, _Enc, _CL=0) ->
+ [];
+format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF, Enc, CL) when is_integer(A) ->
+ Cs = io_lib:fwrite(<<"~s~s ~ts ~ts">>,
+ [sep(N, S), origin(N, M, F, A),
+ mfa_to_string(M, F, A, Enc),
+ location(L)]),
+ CL1 = sub(CL, Cs, Enc),
+ [Cs | format_stacktrace2(S, Fs, N + 1, PF, Enc, CL1)];
+format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF, Enc, CL) when is_list(As) ->
A = length(As),
CalledAs = [S,<<" called as ">>],
- C = format_call("", CalledAs, {M,F}, As, PF, Enc),
- [io_lib:fwrite(<<"~s~s ~ts\n~s~ts">>,
- [sep(N, S), origin(N, M, F, A),
- mfa_to_string(M, F, A, Enc),
- CalledAs, C])
- | format_stacktrace2(S, Fs, N + 1, PF, Enc)];
-format_stacktrace2(_S, [], _N, _PF, _Enc) ->
+ C = format_call("", CalledAs, {M,F}, As, PF, Enc, CL),
+ Cs = io_lib:fwrite(<<"~s~s ~ts\n~s~ts">>,
+ [sep(N, S), origin(N, M, F, A),
+ mfa_to_string(M, F, A, Enc),
+ CalledAs, C]),
+ CL1 = sub(CL, Enc, Cs),
+ [Cs | format_stacktrace2(S, Fs, N + 1, PF, Enc, CL1)];
+format_stacktrace2(_S, [], _N, _PF, _Enc, _CL) ->
"".
location(L) ->
@@ -264,22 +288,26 @@ origin(1, M, F, A) ->
origin(_N, _M, _F, _A) ->
<<"in call from">>.
-format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0, Enc) ->
+format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0, Enc, CL) ->
Pre1 = [Pre0 | n_spaces(exited_size(Class))],
- format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc).
+ format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc, CL).
format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc) ->
+ format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc, -1).
+
+format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc, CL) ->
Arity = length(As),
[ErrStr |
case is_op(ForMForFun, Arity) of
{yes,Op} ->
- format_op(ErrStr, Pre1, Op, As, PF, Enc);
+ format_op(ErrStr, Pre1, Op, As, PF, Enc, CL);
no ->
MFs = mf_to_string(ForMForFun, Arity, Enc),
I1 = string:length([Pre1,ErrStr|MFs]),
- S1 = pp_arguments(PF, As, I1, Enc),
- S2 = pp_arguments(PF, As, string:length([Pre1|MFs]), Enc),
- Long = count_nl(pp_arguments(PF, [a2345,b2345], I1, Enc)) > 0,
+ S1 = pp_arguments(PF, As, I1, Enc, CL),
+ S2 = pp_arguments(PF, As, string:length([Pre1|MFs]), Enc, CL),
+ S3 = pp_arguments(PF, [a2345,b2345], I1, Enc, CL),
+ Long = count_nl(S3) > 0,
case Long or (count_nl(S2) < count_nl(S1)) of
true ->
[$\n, Pre1, MFs, S2];
@@ -288,14 +316,15 @@ format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc) ->
end
end].
-format_op(ErrStr, Pre, Op, [A1], PF, _Enc) ->
+format_op(ErrStr, Pre, Op, [A1], PF, _Enc, CL) ->
OpS = io_lib:fwrite(<<"~s ">>, [Op]),
I1 = iolist_size([ErrStr,Pre,OpS]),
- [OpS | PF(A1, I1+1)];
-format_op(ErrStr, Pre, Op, [A1, A2], PF, Enc) ->
+ {S, _} = PF(A1, I1+1, CL),
+ [OpS | S];
+format_op(ErrStr, Pre, Op, [A1, A2], PF, Enc, CL) ->
I1 = iolist_size([ErrStr,Pre]),
- S1 = PF(A1, I1+1),
- S2 = PF(A2, I1+1),
+ {S1, CL1} = PF(A1, I1+1, CL),
+ {S2, _} = PF(A2, I1+1, CL1),
OpS = atom_to_list(Op),
Pre1 = [$\n | n_spaces(I1)],
case count_nl(S1) > 0 of
@@ -304,26 +333,28 @@ format_op(ErrStr, Pre, Op, [A1, A2], PF, Enc) ->
false ->
OpS2 = io_lib:fwrite(<<" ~s ">>, [Op]),
Size1 = iolist_size([ErrStr,Pre|OpS2]),
- {Size2,S1_2} = size(Enc, S1),
- S2_2 = PF(A2, Size1+Size2+1),
+ Size2 = size(Enc, S1),
+ {S2_2, _} = PF(A2, Size1+Size2+1, CL1),
case count_nl(S2) < count_nl(S2_2) of
true ->
- [S1_2,Pre1,OpS,Pre1|S2];
+ [S1,Pre1,OpS,Pre1|S2];
false ->
- [S1_2,OpS2|S2_2]
+ [S1,OpS2|S2_2]
end
end.
-pp_arguments(PF, As, I, Enc) ->
+pp_arguments(PF, As, I, Enc, CL) ->
case {As, printable_list(Enc, As)} of
{[Int | T], true} ->
L = integer_to_list(Int),
Ll = length(L),
A = list_to_atom(lists:duplicate(Ll, $a)),
- S0 = unicode:characters_to_list(PF([A | T], I+1), Enc),
- brackets_to_parens([$[,L,string:slice(S0, 1+Ll)], Enc);
+ {S0, _} = PF([A | T], I+1, CL),
+ S = unicode:characters_to_list(S0, Enc),
+ brackets_to_parens([$[,L,string:slice(S, 1+Ll)], Enc);
_ ->
- brackets_to_parens(PF(As, I+1), Enc)
+ {S, _CL1} = PF(As, I+1, CL),
+ brackets_to_parens(S, Enc)
end.
brackets_to_parens(S, Enc) ->
@@ -361,12 +392,12 @@ mf_to_string(F, _A, Enc) ->
FS = to_string(F, Enc),
io_lib:fwrite(<<"~ts">>, [FS]).
-format_value(V, ErrStr, Class, PF, S) ->
+format_value(V, ErrStr, Class, PF, S, CL) ->
Pre1Sz = exited_size(Class),
- S1 = PF(V, Pre1Sz + iolist_size([S, ErrStr])+1),
+ {S1, _} = PF(V, Pre1Sz + iolist_size([S, ErrStr]) + 1, CL),
[ErrStr | case count_nl(S1) of
N1 when N1 > 1 ->
- S2 = PF(V, iolist_size(S) + 1 + Pre1Sz),
+ {S2, _} = PF(V, iolist_size(S) + 1 + Pre1Sz, CL),
case count_nl(S2) < N1 of
true ->
[$\n, S, n_spaces(Pre1Sz) | S2];
@@ -413,9 +444,17 @@ to_string(A, latin1) ->
to_string(A, _) ->
io_lib:write_atom(A).
+%% Make sure T does change sign.
+sub(T, _, _Enc) when T < 0 -> T;
+sub(T, S, Enc) ->
+ sub(T, size(Enc, S)).
+
+sub(T, Sz) when T >= Sz ->
+ T - Sz;
+sub(_T, _Sz) ->
+ 0.
+
size(latin1, S) ->
- {iolist_size(S),S};
-size(_, S0) ->
- S = unicode:characters_to_list(S0, unicode),
- true = is_list(S),
- {string:length(S),S}.
+ iolist_size(S);
+size(_, S) ->
+ string:length(S).
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 2066b2f60f..aa809ab05c 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -501,13 +501,13 @@ find_maxline(LC) ->
hide_calls(LC, MaxLine) ->
LineId0 = MaxLine + 1,
- {NLC, _, D} = hide(LC, LineId0, dict:new()),
+ {NLC, _, D} = hide(LC, LineId0, maps:new()),
{NLC, D}.
%% v/1 and local calls are hidden.
hide({value,L,V}, Id, D) ->
A = erl_anno:new(Id),
- {{atom,A,ok}, Id+1, dict:store(Id, {value,L,V}, D)};
+ {{atom,A,ok}, Id+1, maps:put(Id, {value,L,V}, D)};
hide({call,L,{atom,_,N}=Atom,Args}, Id0, D0) ->
{NArgs, Id, D} = hide(Args, Id0, D0),
C = case erl_internal:bif(N, length(Args)) of
@@ -517,7 +517,7 @@ hide({call,L,{atom,_,N}=Atom,Args}, Id0, D0) ->
A = erl_anno:new(Id),
{call,A,{remote,L,{atom,L,m},{atom,L,f}},NArgs}
end,
- {C, Id+1, dict:store(Id, {call,Atom}, D)};
+ {C, Id+1, maps:put(Id, {call,Atom}, D)};
hide(T0, Id0, D0) when is_tuple(T0) ->
{L, Id, D} = hide(tuple_to_list(T0), Id0, D0),
{list_to_tuple(L), Id, D};
@@ -532,7 +532,7 @@ unhide_calls({atom,A,ok}=E, MaxLine, D) ->
L = erl_anno:line(A),
if
L > MaxLine ->
- dict:fetch(L, D);
+ map_get(L, D);
true ->
E
end;
@@ -540,7 +540,7 @@ unhide_calls({call,A,{remote,L,{atom,L,m},{atom,L,f}}=F,Args}, MaxLine, D) ->
Line = erl_anno:line(A),
if
Line > MaxLine ->
- {call,Atom} = dict:fetch(Line, D),
+ {call,Atom} = map_get(Line, D),
{call,L,Atom,unhide_calls(Args, MaxLine, D)};
true ->
{call,A,F,unhide_calls(Args, MaxLine, D)}
@@ -1163,9 +1163,19 @@ match1({map,_,Fs}, #{}=Map, Bs, BBs) ->
match1({map,_,_}, _, _Bs, _BBs) ->
throw(nomatch);
match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) ->
- eval_bits:match_bits(Fs, B, Bs0, BBs,
- match_fun(BBs),
- fun(E, Bs) -> expr(E, Bs, none, none, none) end);
+ EvalFun = fun(E, Bs) ->
+ case erl_lint:is_guard_expr(E) of
+ true -> ok;
+ false -> throw(invalid)
+ end,
+ try
+ expr(E, Bs, none, none, none)
+ catch
+ error:{unbound, _} ->
+ throw(invalid)
+ end
+ end,
+ eval_bits:match_bits(Fs, B, Bs0, BBs, match_fun(BBs), EvalFun);
match1({bin,_,_}, _, _Bs, _BBs) ->
throw(nomatch);
match1({op,_,'++',{nil,_},R}, Term, Bs, BBs) ->
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index d7bd15d9db..6200978d4d 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -20,10 +20,6 @@
%% Purpose: Expand records into tuples. Also add explicit module
%% names to calls to imported functions and BIFs.
-%% N.B. Although structs (tagged tuples) are not yet allowed in the
-%% language there is code included in pattern/2 and expr/3 (commented out)
-%% that handles them.
-
-module(erl_expand_records).
-export([module/2]).
@@ -125,9 +121,6 @@ pattern({map_field_exact,Line,K0,V0}, St0) ->
{K,St1} = expr(K0, St0),
{V,St2} = pattern(V0, St1),
{{map_field_exact,Line,K,V},St2};
-%%pattern({struct,Line,Tag,Ps}, St0) ->
-%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
-%% {{struct,Line,Tag,TPs},TPsvs,St1};
pattern({record_index,Line,Name,Field}, St) ->
{index_expr(Line, Field, Name, record_fields(Name, St)),St};
pattern({record,Line0,Name,Pfs}, St0) ->
@@ -310,9 +303,6 @@ expr({map_field_exact,Line,K0,V0}, St0) ->
{K,St1} = expr(K0, St0),
{V,St2} = expr(V0, St1),
{{map_field_exact,Line,K,V},St2};
-%%expr({struct,Line,Tag,Es0}, Vs, St0) ->
-%% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
-%% {{struct,Line,Tag,Es1},Esvs,Esus,St1};
expr({record_index,Line,Name,F}, St) ->
I = index_expr(Line, F, Name, record_fields(Name, St)),
expr(I, St);
@@ -797,9 +787,13 @@ is_simple_val(Val) ->
pattern_bin(Es0, St) ->
foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],St}, Es0).
-pattern_element({bin_element,Line,Expr0,Size,Type}, {Es,St0}) ->
+pattern_element({bin_element,Line,Expr0,Size0,Type}, {Es,St0}) ->
{Expr,St1} = pattern(Expr0, St0),
- {[{bin_element,Line,Expr,Size,Type} | Es],St1}.
+ {Size,St2} = case Size0 of
+ default -> {Size0,St1};
+ _ -> expr(Size0, St1)
+ end,
+ {[{bin_element,Line,Expr,Size,Type} | Es],St2}.
%% expr_bin([Element], State) -> {[Element],State}.
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 939abaff00..6ff5e23ee3 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -245,11 +245,14 @@ bif(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> false.
bif(abs, 1) -> true;
bif(apply, 2) -> true;
bif(apply, 3) -> true;
+bif(atom_to_binary, 1) -> 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, 1) -> true;
bif(binary_to_atom, 2) -> true;
+bif(binary_to_existing_atom, 1) -> true;
bif(binary_to_existing_atom, 2) -> true;
bif(binary_to_integer, 1) -> true;
bif(binary_to_integer, 2) -> true;
@@ -308,7 +311,6 @@ bif(is_process_alive, 1) -> true;
bif(is_atom, 1) -> true;
bif(is_boolean, 1) -> true;
bif(is_binary, 1) -> true;
-bif(is_bitstr, 1) -> true;
bif(is_bitstring, 1) -> true;
bif(is_float, 1) -> true;
bif(is_function, 1) -> true;
@@ -345,7 +347,6 @@ 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;
@@ -383,8 +384,16 @@ bif(spawn_link, 1) -> true;
bif(spawn_link, 2) -> true;
bif(spawn_link, 3) -> true;
bif(spawn_link, 4) -> true;
+bif(spawn_request, 1) -> true;
+bif(spawn_request, 2) -> true;
+bif(spawn_request, 3) -> true;
+bif(spawn_request, 4) -> true;
+bif(spawn_request, 5) -> true;
+bif(spawn_request_abandon, 1) -> true;
bif(spawn_monitor, 1) -> true;
+bif(spawn_monitor, 2) -> true;
bif(spawn_monitor, 3) -> true;
+bif(spawn_monitor, 4) -> true;
bif(spawn_opt, 2) -> true;
bif(spawn_opt, 3) -> true;
bif(spawn_opt, 4) -> true;
@@ -393,6 +402,8 @@ bif(split_binary, 2) -> true;
bif(statistics, 1) -> true;
bif(term_to_binary, 1) -> true;
bif(term_to_binary, 2) -> true;
+bif(term_to_iovec, 1) -> true;
+bif(term_to_iovec, 2) -> true;
bif(throw, 1) -> true;
bif(time, 0) -> true;
bif(tl, 1) -> true;
@@ -452,7 +463,6 @@ 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;
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 54b0fbd999..7c717e47d1 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -1,8 +1,8 @@
-%% -*- erlang-indent-level: 4 -*-
+%%% -*- erlang-indent-level: 4 -*-
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,9 +20,6 @@
%%
%% Do necessary checking of Erlang code.
-%% N.B. All the code necessary for checking structs (tagged tuples) is
-%% here. Just comment out the lines in pattern/2, gexpr/3 and expr/3.
-
-module(erl_lint).
-export([module/1,module/2,module/3,format_error/1]).
@@ -31,7 +28,14 @@
-export([is_guard_expr/1]).
-export([bool_option/4,value_option/3,value_option/7]).
--import(lists, [member/2,map/2,foldl/3,foldr/3,mapfoldl/3,all/2,reverse/1]).
+-import(lists, [all/2,any/2,
+ foldl/3,foldr/3,
+ map/2,mapfoldl/3,member/2,
+ reverse/1]).
+
+%% Removed functions
+
+-removed([{modify_line,2,"use erl_parse:map_anno/2 instead"}]).
%% bool_option(OnOpt, OffOpt, Default, Options) -> boolean().
%% value_option(Flag, Default, Options) -> Value.
@@ -81,17 +85,19 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
-type module_or_mfa() :: module() | mfa().
+-type gexpr_context() :: 'guard' | 'bin_seg_size' | 'map_key'.
+
-record(typeinfo, {attr, line}).
%% Usage of records, functions, and imports. The variable table, which
%% is passed on as an argument, holds the usage of variables.
-record(usage, {
- calls = dict:new(), %Who calls who
+ calls = maps:new(), %Who calls who
imported = [], %Actually imported functions
- used_records = sets:new() %Used record definitions
- :: sets:set(atom()),
- used_types = dict:new() %Used type definitions
- :: dict:dict(ta(), line())
+ used_records = gb_sets:new() %Used record definitions
+ :: gb_sets:set(atom()),
+ used_types = maps:new() %Used type definitions
+ :: #{ta() := line()}
}).
@@ -104,8 +110,8 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
exports=gb_sets:empty() :: gb_sets:set(fa()),%Exports
imports=[] :: orddict:orddict(fa(), module()),%Imports
compile=[], %Compile flags
- records=dict:new() %Record definitions
- :: dict:dict(atom(), {line(),Fields :: term()}),
+ records=maps:new() %Record definitions
+ :: #{atom() => {line(),Fields :: term()}},
locals=gb_sets:empty() %All defined functions (prescanned)
:: gb_sets:set(fa()),
no_auto=gb_sets:empty() %Functions explicitly not autoimported
@@ -131,17 +137,20 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
xqlc= false :: boolean(), %true if qlc.hrl included
called= [] :: [{fa(),line()}], %Called functions
usage = #usage{} :: #usage{},
- specs = dict:new() %Type specifications
- :: dict:dict(mfa(), line()),
- callbacks = dict:new() %Callback types
- :: dict:dict(mfa(), line()),
- optional_callbacks = dict:new() %Optional callbacks
- :: dict:dict(mfa(), line()),
- types = dict:new() %Type definitions
- :: dict:dict(ta(), #typeinfo{}),
+ specs = maps:new() %Type specifications
+ :: #{mfa() => line()},
+ callbacks = maps:new() %Callback types
+ :: #{mfa() => line()},
+ optional_callbacks = maps:new() %Optional callbacks
+ :: #{mfa() => line()},
+ types = maps:new() %Type definitions
+ :: #{ta() => #typeinfo{}},
exp_types=gb_sets:empty() %Exported types
:: gb_sets:set(ta()),
- in_try_head=false :: boolean() %In a try head.
+ in_try_head=false :: boolean(), %In a try head.
+ bvt = none :: 'none' | [any()], %Variables in binary pattern
+ gexpr_context = guard %Context of guard expression
+ :: gexpr_context()
}).
-type lint_state() :: #lint{}.
@@ -183,6 +192,14 @@ format_error({invalid_deprecated,D}) ->
format_error({bad_deprecated,{F,A}}) ->
io_lib:format("deprecated function ~tw/~w undefined or not exported",
[F,A]);
+format_error({invalid_removed,D}) ->
+ io_lib:format("badly formed removed attribute ~tw", [D]);
+format_error({bad_removed,{F,A}}) when F =:= '_'; A =:= '_' ->
+ io_lib:format("at least one function matching ~tw/~w is still exported",
+ [F,A]);
+format_error({bad_removed,{F,A}}) ->
+ io_lib:format("removed function ~tw/~w is still exported",
+ [F,A]);
format_error({bad_nowarn_unused_function,{F,A}}) ->
io_lib:format("function ~tw/~w undefined", [F,A]);
format_error({bad_nowarn_bif_clash,{F,A}}) ->
@@ -198,6 +215,9 @@ format_error({bad_on_load_arity,{F,A}}) ->
io_lib:format("function ~tw/~w has wrong arity (must be 0)", [F,A]);
format_error({undefined_on_load,{F,A}}) ->
io_lib:format("function ~tw/~w undefined", [F,A]);
+format_error(nif_inline) ->
+ "inlining is enabled - local calls to NIFs may call their Erlang "
+ "implementation instead";
format_error(export_all) ->
"export_all flag enabled - all functions will be exported";
@@ -228,21 +248,21 @@ format_error({redefine_old_bif_import,{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}) ->
+format_error({deprecated, MFA, String, Rel}) ->
io_lib:format("~s is deprecated and will be removed in ~s; use ~s",
- [format_mfa(MFA), Rel, format_mfa(ReplacementMFA)]);
-format_error({deprecated, {M1, F1, A1}, String}) when is_list(String) ->
- io_lib:format("~p:~p/~p: ~s", [M1, F1, A1, String]);
+ [format_mfa(MFA), Rel, String]);
+format_error({deprecated, MFA, String}) when is_list(String) ->
+ io_lib:format("~s is deprecated; ~s", [format_mfa(MFA), String]);
format_error({deprecated_type, {M1, F1, A1}, String}) when is_list(String) ->
- io_lib:format("~p:~p~s: ~s", [M1, F1, gen_type_paren(A1), String]);
+ io_lib:format("the type ~p:~p~s is deprecated; ~s",
+ [M1, F1, gen_type_paren(A1), String]);
format_error({removed, MFA, ReplacementMFA, Rel}) ->
io_lib:format("call to ~s will fail, since it was removed in ~s; "
"use ~s", [format_mfa(MFA), Rel, format_mfa(ReplacementMFA)]);
format_error({removed, MFA, String}) when is_list(String) ->
- io_lib:format("~s: ~s", [format_mfa(MFA), String]);
-format_error({removed_type, MNA, ReplacementMNA, Rel}) ->
- io_lib:format("the type ~s was removed in ~s; use ~s instead",
- [format_mna(MNA), Rel, format_mna(ReplacementMNA)]);
+ io_lib:format("~s is removed; ~s", [format_mfa(MFA), String]);
+format_error({removed_type, MNA, String}) ->
+ io_lib:format("the type ~s is removed; ~s", [format_mna(MNA), String]);
format_error({obsolete_guard, {F, A}}) ->
io_lib:format("~p/~p obsolete (use is_~p/~p)", [F, A, F, A]);
format_error({obsolete_guard_overridden,Test}) ->
@@ -272,6 +292,8 @@ format_error({redefine_record,T}) ->
io_lib:format("record ~tw already defined", [T]);
format_error({redefine_field,T,F}) ->
io_lib:format("field ~tw already defined in record ~tw", [F,T]);
+format_error(bad_multi_field_init) ->
+ io_lib:format("'_' initializes no omitted fields", []);
format_error({undefined_field,T,F}) ->
io_lib:format("field ~tw undefined in record ~tw", [F,T]);
format_error(illegal_record_info) ->
@@ -313,6 +335,13 @@ format_error(bittype_unit) ->
"a bit unit size must not be specified unless a size is specified too";
format_error(illegal_bitsize) ->
"illegal bit size";
+format_error({illegal_bitsize_local_call, {F,A}}) ->
+ io_lib:format("call to local/imported function ~tw/~w is illegal in a size "
+ "expression for a binary segment",
+ [F,A]);
+format_error(non_integer_bitsize) ->
+ "a size expression in a pattern evaluates to a non-integer value; "
+ "this pattern cannot possibly match";
format_error(unsized_binary_not_at_end) ->
"a binary field without size is only allowed at the end of a binary pattern";
format_error(typed_literal_string) ->
@@ -585,13 +614,16 @@ start(File, Opts) ->
false, Opts)},
{removed,
bool_option(warn_removed, nowarn_removed,
+ true, Opts)},
+ {nif_inline,
+ bool_option(warn_nif_inline, nowarn_nif_inline,
true, Opts)}
],
Enabled1 = [Category || {Category,true} <- Enabled0],
Enabled = ordsets:from_list(Enabled1),
Calls = case ordsets:is_element(unused_function, Enabled) of
true ->
- dict:from_list([{{module_info,1},pseudolocals()}]);
+ maps:from_list([{{module_info,1},pseudolocals()}]);
false ->
undefined
end,
@@ -647,7 +679,14 @@ pack_warnings(Ws) ->
add_error(E, St) -> add_lint_error(E, St#lint.file, St).
-add_error(Anno, E, St) ->
+add_error(Anno, E0, #lint{gexpr_context=Context}=St) ->
+ E = case {E0,Context} of
+ {illegal_guard_expr,bin_seg_size} ->
+ illegal_bitsize;
+ {{illegal_guard_local_call,FA},bin_seg_size} ->
+ {illegal_bitsize_local_call,FA};
+ {_,_} -> E0
+ end,
{File,Location} = loc(Anno, St),
add_lint_error({Location,erl_lint,E}, File, St).
@@ -918,7 +957,8 @@ post_traversal_check(Forms, St0) ->
StE = check_unused_records(Forms, StD),
StF = check_local_opaque_types(StE),
StG = check_dialyzer_attribute(Forms, StF),
- check_callback_information(StG).
+ StH = check_callback_information(StG),
+ check_removed(Forms, StH).
%% check_behaviour(State0) -> State
%% Check that the behaviour attribute is valid.
@@ -1030,7 +1070,7 @@ check_deprecated(Forms, St0) ->
true -> St0#lint.defined;
false -> St0#lint.exports
end,
- X = gb_sets:to_list(Exports),
+ X = ignore_predefined_funcs(gb_sets:to_list(Exports)),
#lint{module = Mod} = St0,
Bad = [{E,L} || {attribute, L, deprecated, Depr} <- Forms,
D <- lists:flatten([Depr]),
@@ -1074,7 +1114,80 @@ depr_fa(F, A, _X, _Mod) ->
deprecated_flag(next_version) -> true;
deprecated_flag(next_major_release) -> true;
deprecated_flag(eventually) -> true;
-deprecated_flag(_) -> false.
+deprecated_flag(String) -> deprecated_desc(String).
+
+deprecated_desc([Char | Str]) when is_integer(Char) -> deprecated_desc(Str);
+deprecated_desc([]) -> true;
+deprecated_desc(_) -> false.
+
+%% check_removed(Forms, State0) -> State
+
+check_removed(Forms, St0) ->
+ %% Get the correct list of exported functions.
+ Exports = case member(export_all, St0#lint.compile) of
+ true -> St0#lint.defined;
+ false -> St0#lint.exports
+ end,
+ X = ignore_predefined_funcs(gb_sets:to_list(Exports)),
+ #lint{module = Mod} = St0,
+ Bad = [{E,L} || {attribute, L, removed, Removed} <- Forms,
+ R <- lists:flatten([Removed]),
+ E <- removed_cat(R, X, Mod)],
+ foldl(fun ({E,L}, St1) ->
+ add_error(L, E, St1)
+ end, St0, Bad).
+
+removed_cat({F, A, Desc}=R, X, Mod) ->
+ case removed_desc(Desc) of
+ false -> [{invalid_removed,R}];
+ true -> removed_fa(F, A, X, Mod)
+ end;
+removed_cat({F, A}, X, Mod) ->
+ removed_fa(F, A, X, Mod);
+removed_cat(module, X, Mod) ->
+ removed_fa('_', '_', X, Mod);
+removed_cat(R, _X, _Mod) ->
+ [{invalid_removed,R}].
+
+removed_fa('_', '_', X, _Mod) ->
+ case X of
+ [_|_] -> [{bad_removed,{'_','_'}}];
+ [] -> []
+ end;
+removed_fa(F, '_', X, _Mod) when is_atom(F) ->
+ %% Don't use this syntax for built-in functions.
+ case lists:filter(fun({F1,_}) -> F1 =:= F end, X) of
+ [_|_] -> [{bad_removed,{F,'_'}}];
+ _ -> []
+ end;
+removed_fa(F, A, X, Mod) when is_atom(F), is_integer(A), A >= 0 ->
+ case lists:member({F,A}, X) of
+ true ->
+ [{bad_removed,{F,A}}];
+ false ->
+ case erlang:is_builtin(Mod, F, A) of
+ true -> [{bad_removed,{F,A}}];
+ false -> []
+ end
+ end;
+removed_fa(F, A, _X, _Mod) ->
+ [{invalid_removed,{F,A}}].
+
+removed_desc([Char | Str]) when is_integer(Char) -> removed_desc(Str);
+removed_desc([]) -> true;
+removed_desc(_) -> false.
+
+%% Ignores functions added by erl_internal:add_predefined_functions/1
+ignore_predefined_funcs([{behaviour_info,1} | Fs]) ->
+ ignore_predefined_funcs(Fs);
+ignore_predefined_funcs([{module_info,0} | Fs]) ->
+ ignore_predefined_funcs(Fs);
+ignore_predefined_funcs([{module_info,1} | Fs]) ->
+ ignore_predefined_funcs(Fs);
+ignore_predefined_funcs([Other | Fs]) ->
+ [Other | ignore_predefined_funcs(Fs)];
+ignore_predefined_funcs([]) ->
+ [].
%% check_imports(Forms, State0) -> State
@@ -1134,7 +1247,7 @@ reached_functions([R|Rs], More0, Ref, Reached0) ->
true -> reached_functions(Rs, More0, Ref, Reached0);
false ->
Reached = gb_sets:add_element(R, Reached0), %It IS reached
- case dict:find(R, Ref) of
+ case maps:find(R, Ref) of
{ok,More} -> reached_functions(Rs, [More|More0], Ref, Reached);
error -> reached_functions(Rs, More0, Ref, Reached)
end
@@ -1157,10 +1270,10 @@ check_undefined_functions(#lint{called=Called0,defined=Def0}=St0) ->
check_undefined_types(#lint{usage=Usage,types=Def}=St0) ->
Used = Usage#usage.used_types,
- UTAs = dict:fetch_keys(Used),
- Undef = [{TA,dict:fetch(TA, Used)} ||
+ UTAs = maps:keys(Used),
+ Undef = [{TA,map_get(TA, Used)} ||
TA <- UTAs,
- not dict:is_key(TA, Def),
+ not is_map_key(TA, Def),
not is_default_type(TA)],
foldl(fun ({TA,L}, St) ->
add_error(L, {undefined_type,TA}, St)
@@ -1199,7 +1312,7 @@ check_untyped_records(Forms, St0) ->
case is_warn_enabled(untyped_record, St0) of
true ->
%% Use the names of all records *defined* in the module (not used)
- RecNames = dict:fetch_keys(St0#lint.records),
+ RecNames = maps:keys(St0#lint.records),
%% these are the records with field(s) containing type info
TRecNames = [Name ||
{attribute,_,record,{Name,Fields}} <- Forms,
@@ -1207,7 +1320,7 @@ check_untyped_records(Forms, St0) ->
(_) -> false
end, Fields)],
foldl(fun (N, St) ->
- {L, Fields} = dict:fetch(N, St0#lint.records),
+ {L, Fields} = map_get(N, St0#lint.records),
case Fields of
[] -> St; % exclude records with no fields
[_|_] -> add_warning(L, {untyped_record, N}, St)
@@ -1225,12 +1338,12 @@ check_unused_records(Forms, St0) ->
%% The check is a bit imprecise in that uses from unused
%% functions count.
Usage = St0#lint.usage,
- UsedRecords = sets:to_list(Usage#usage.used_records),
- URecs = foldl(fun (Used, Recs) ->
- dict:erase(Used, Recs)
- end, St0#lint.records, UsedRecords),
+ UsedRecords = Usage#usage.used_records,
+ URecs = gb_sets:fold(fun (Used, Recs) ->
+ maps:remove(Used, Recs)
+ end, St0#lint.records, UsedRecords),
Unused = [{Name,FileLine} ||
- {Name,{FileLine,_Fields}} <- dict:to_list(URecs),
+ {Name,{FileLine,_Fields}} <- maps:to_list(URecs),
element(1, loc(FileLine, St0)) =:= FirstFile],
foldl(fun ({N,L}, St) ->
add_warning(L, {unused_record, N}, St)
@@ -1242,27 +1355,26 @@ check_unused_records(Forms, St0) ->
check_callback_information(#lint{callbacks = Callbacks,
optional_callbacks = OptionalCbs,
defined = Defined} = St0) ->
- OptFun = fun({MFA, Line}, St) ->
- case dict:is_key(MFA, Callbacks) of
+ OptFun = fun(MFA, Line, St) ->
+ case is_map_key(MFA, Callbacks) of
true ->
St;
false ->
add_error(Line, {undefined_callback, MFA}, St)
end
end,
- St1 = lists:foldl(OptFun, St0, dict:to_list(OptionalCbs)),
+ St1 = maps:fold(OptFun, St0, OptionalCbs),
case gb_sets:is_member({behaviour_info, 1}, Defined) of
false -> St1;
true ->
- case dict:size(Callbacks) of
+ case map_size(Callbacks) of
0 -> St1;
_ ->
- CallbacksList = dict:to_list(Callbacks),
- FoldL =
- fun({Fa, Line}, St) ->
+ FoldFun =
+ fun(Fa, Line, St) ->
add_error(Line, {behaviour_info, Fa}, St)
end,
- lists:foldl(FoldL, St1, CallbacksList)
+ maps:fold(FoldFun, St1, Callbacks)
end
end.
@@ -1300,7 +1412,7 @@ export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) ->
false ->
St2
end,
- {gb_sets:add_element(TA, E), dict:store(TA, Line, U), St}
+ {gb_sets:add_element(TA, E), maps:put(TA, Line, U), St}
end,
{ETs0,UTs0,St0}, ETs) of
{ETs1,UTs1,St1} ->
@@ -1430,7 +1542,7 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func,file=File}=St)
NA = {F,A},
Usage = case Cs of
undefined -> Usage0;
- _ -> Usage0#usage{calls=dict:append(Func, NA, Cs)}
+ _ -> Usage0#usage{calls=maps_prepend(Func, NA, Cs)}
end,
Anno = erl_anno:set_file(File, Line),
St#lint{called=[{NA,Anno}|Cd], usage=Usage}.
@@ -1531,8 +1643,6 @@ pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_list(Ps, Vt, Old, Bvt, St);
pattern({map,_Line,Ps}, Vt, Old, Bvt, St) ->
pattern_map(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} =
check_record(Line, Name, St,
@@ -1541,10 +1651,11 @@ pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
end),
{Vt1,[],St1};
pattern({record,Line,Name,Pfs}, Vt, Old, Bvt, St) ->
- case dict:find(Name, St#lint.records) of
+ case maps:find(Name, St#lint.records) of
{ok,{_Line,Fields}} ->
St1 = used_record(Name, St),
- pattern_fields(Pfs, Name, Fields, Vt, Old, Bvt, St1);
+ St2 = check_multi_field_init(Pfs, Line, Fields, St1),
+ pattern_fields(Pfs, Name, Fields, Vt, Old, Bvt, St2);
error -> {[],[],add_error(Line, {undefined_record,Name}, St)}
end;
pattern({bin,_,Fs}, Vt, Old, Bvt, St) ->
@@ -1575,7 +1686,14 @@ pattern_list(Ps, Vt, Old, Bvt0, St) ->
{vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt,Bvt1),St1}
end, {[],[],St}, Ps).
-
+%% Check for '_' initializing no fields.
+check_multi_field_init(Fs, Line, Fields, St) ->
+ case
+ has_wildcard_field(Fs) andalso init_fields(Fs, Line, Fields) =:= []
+ of
+ true -> add_error(Line, bad_multi_field_init, St);
+ false -> St
+ end.
%% reject_invalid_alias(Pat, Expr, Vt, St) -> St'
%% Reject aliases for binary patterns at the top level.
@@ -1625,10 +1743,10 @@ reject_invalid_alias({tuple,_,Es1}, {tuple,_,Es2}, Vt, St) ->
reject_invalid_alias_list(Es1, Es2, Vt, St);
reject_invalid_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2}, Vt,
#lint{records=Recs}=St) ->
- case {dict:find(Name1, Recs),dict:find(Name2, Recs)} of
- {{ok,{_Line1,Fields1}},{ok,{_Line2,Fields2}}} ->
+ case Recs of
+ #{Name1 := {_Line1,Fields1}, Name2 := {_Line2,Fields2}} ->
reject_invalid_alias_rec(Pfs1, Pfs2, Fields1, Fields2, Vt, St);
- {_,_} ->
+ #{} ->
%% One or more non-existing records. (An error messages has
%% already been generated, so we are done here.)
St
@@ -1706,19 +1824,16 @@ is_pattern_expr_1({op,_Line,Op,A1,A2}) ->
is_pattern_expr_1(_Other) -> false.
pattern_map(Ps, Vt, Old, Bvt, St) ->
- foldl(fun
- ({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) ->
- {Psvt,Bvt0,add_error(L, illegal_pattern, St0)};
- ({map_field_exact,L,K,V}, {Psvt,Bvt0,St0}) ->
- case is_valid_map_key(K) of
- true ->
- {Kvt,St1} = expr(K, Vt, St0),
- {Vvt,Bvt2,St2} = pattern(V, Vt, Old, Bvt, St1),
- {vtmerge_pat(vtmerge_pat(Kvt, Vvt), Psvt), vtmerge_pat(Bvt0, Bvt2), St2};
- false ->
- {Psvt,Bvt0,add_error(L, illegal_map_key, St0)}
- end
- end, {[],[],St}, Ps).
+ foldl(fun({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) ->
+ {Psvt,Bvt0,add_error(L, illegal_pattern, St0)};
+ ({map_field_exact,_L,K,V}, {Psvt,Bvt0,St0}) ->
+ St1 = St0#lint{gexpr_context=map_key},
+ {Kvt,St2} = gexpr(K, Vt, St1),
+ {Vvt,Bvt2,St3} = pattern(V, Vt, Old, Bvt, St2),
+ {vtmerge_pat(vtmerge_pat(Kvt, Vvt), Psvt),
+ vtmerge_pat(Bvt0, Bvt2),
+ St3}
+ end, {[],[],St}, Ps).
%% pattern_bin([Element], VarTable, Old, BinVarTable, State) ->
%% {UpdVarTable,UpdBinVarTable,State}.
@@ -1787,21 +1902,41 @@ pat_bit_expr(P, _Old, _Bvt, St) ->
%% Check pattern size expression, only allow really valid sizes!
pat_bit_size(default, _Vt, _Bvt, St) -> {default,[],[],St};
-pat_bit_size({atom,_Line,all}, _Vt, _Bvt, St) -> {all,[],[],St};
pat_bit_size({var,Lv,V}, Vt0, Bvt0, St0) ->
{Vt,Bvt,St1} = pat_binsize_var(V, Lv, Vt0, Bvt0, St0),
{unknown,Vt,Bvt,St1};
-pat_bit_size(Size, _Vt, _Bvt, St) ->
+pat_bit_size(Size, Vt0, Bvt0, St0) ->
Line = element(2, Size),
- case is_pattern_expr(Size) of
- true ->
- case erl_eval:partial_eval(Size) of
- {integer,Line,I} -> {I,[],[],St};
- _Other -> {unknown,[],[],add_error(Line, illegal_bitsize, St)}
- end;
- false -> {unknown,[],[],add_error(Line, illegal_bitsize, St)}
+ case erl_eval:partial_eval(Size) of
+ {integer,Line,I} -> {I,[],[],St0};
+ Expr ->
+ %% The size is an expression using operators
+ %% and/or guard BIFs calls. If the expression
+ %% happens to evaluate to a non-integer value, the
+ %% pattern will fail to match.
+ St1 = St0#lint{bvt=Bvt0,gexpr_context=bin_seg_size},
+ {Vt,#lint{bvt=Bvt}=St2} = gexpr(Size, Vt0, St1),
+ St3 = St2#lint{bvt=none,gexpr_context=St0#lint.gexpr_context},
+ St = case is_bit_size_illegal(Expr) of
+ true ->
+ %% The size is a non-integer literal or a simple
+ %% expression that does not evaluate to an
+ %% integer value. Issue a warning.
+ add_warning(Line, non_integer_bitsize, St3);
+ false -> St3
+ end,
+ {unknown,Vt,Bvt,St}
end.
+is_bit_size_illegal({atom,_,_}) -> true;
+is_bit_size_illegal({bin,_,_}) -> true;
+is_bit_size_illegal({cons,_,_,_}) -> true;
+is_bit_size_illegal({float,_,_}) -> true;
+is_bit_size_illegal({map,_,_}) -> true;
+is_bit_size_illegal({nil,_}) -> true;
+is_bit_size_illegal({tuple,_,_}) -> true;
+is_bit_size_illegal(_) -> false.
+
%% expr_bin(Line, [Element], VarTable, State, CheckFun) -> {UpdVarTable,State}.
%% Check an expression group.
@@ -2067,7 +2202,7 @@ gexpr_list(Es, Vt, St) ->
Expr :: erl_parse:abstract_expr().
is_guard_test(E) ->
- is_guard_test2(E, {dict:new(),fun(_) -> false end}).
+ is_guard_test2(E, {maps:new(),fun(_) -> false end}).
%% is_guard_test(Expression, Forms) -> boolean().
is_guard_test(Expression, Forms) ->
@@ -2115,7 +2250,7 @@ is_guard_test2(G, Info) ->
%% 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, {[],fun({_,_}) -> false end}).
is_gexpr({var,_L,_V}, _Info) -> true;
is_gexpr({char,_L,_C}, _Info) -> true;
@@ -2126,8 +2261,6 @@ is_gexpr({string,_L,_S}, _Info) -> true;
is_gexpr({nil,_L}, _Info) -> true;
is_gexpr({cons,_L,H,T}, Info) -> is_gexpr_list([H,T], Info);
is_gexpr({tuple,_L,Es}, Info) -> is_gexpr_list(Es, Info);
-%%is_gexpr({struct,_L,_Tag,Es}, Info) ->
-%% is_gexpr_list(Es, Info);
is_gexpr({map,_L,Es}, Info) ->
is_map_fields(Es, Info);
is_gexpr({map,_L,Src,Es}, Info) ->
@@ -2183,7 +2316,7 @@ is_map_fields([], _Info) -> true;
is_map_fields(_T, _Info) -> false.
is_gexpr_fields(Fs, L, Name, {RDs,_}=Info) ->
- IFs = case dict:find(Name, RDs) of
+ IFs = case maps:find(Name, RDs) of
{ok,{_Line,Fields}} -> Fs ++ init_fields(Fs, L, Fields);
error -> Fs
end,
@@ -2510,73 +2643,16 @@ is_valid_call(Call) ->
_ -> true
end.
-%% is_valid_map_key(K) -> true | false
-%% variables are allowed for patterns only at the top of the tree
-
-is_valid_map_key({var,_,_}) -> true;
-is_valid_map_key(K) -> is_valid_map_key_value(K).
-is_valid_map_key_value(K) ->
- case K of
- {var,_,_} -> false;
- {char,_,_} -> true;
- {integer,_,_} -> true;
- {float,_,_} -> true;
- {string,_,_} -> true;
- {nil,_} -> true;
- {atom,_,_} -> true;
- {cons,_,H,T} ->
- is_valid_map_key_value(H) andalso
- is_valid_map_key_value(T);
- {tuple,_,Es} ->
- foldl(fun(E,B) ->
- B andalso is_valid_map_key_value(E)
- end,true,Es);
- {map,_,Arg,Ps} ->
- % only check for value expressions to be valid
- % invalid map expressions are later checked in
- % core and kernel
- is_valid_map_key_value(Arg) andalso foldl(fun
- ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
- Tag =:= map_field_exact ->
- B andalso is_valid_map_key_value(Ke)
- andalso is_valid_map_key_value(Ve);
- (_,_) -> false
- end,true,Ps);
- {map,_,Ps} ->
- foldl(fun
- ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
- Tag =:= map_field_exact ->
- B andalso is_valid_map_key_value(Ke)
- andalso is_valid_map_key_value(Ve);
- (_,_) -> false
- end, true, Ps);
- {record,_,_,Fs} ->
- foldl(fun
- ({record_field,_,Ke,Ve},B) ->
- B andalso is_valid_map_key_value(Ke)
- andalso is_valid_map_key_value(Ve)
- end,true,Fs);
- {bin,_,Es} ->
- % only check for value expressions to be valid
- % invalid binary expressions are later checked in
- % core and kernel
- foldl(fun
- ({bin_element,_,E,_,_},B) ->
- B andalso is_valid_map_key_value(E)
- end,true,Es);
- Val -> is_pattern_expr(Val)
- end.
-
%% record_def(Line, RecordName, [RecField], State) -> State.
%% Add a record definition if it does not already exist. Normalise
%% so that all fields have explicit initial value.
record_def(Line, Name, Fs0, St0) ->
- case dict:is_key(Name, St0#lint.records) of
+ case is_map_key(Name, St0#lint.records) of
true -> add_error(Line, {redefine_record,Name}, St0);
false ->
{Fs1,St1} = def_fields(normalise_fields(Fs0), Name, St0),
- St2 = St1#lint{records=dict:store(Name, {Line,Fs1},
+ St2 = St1#lint{records=maps:put(Name, {Line,Fs1},
St1#lint.records)},
Types = [T || {typed_record_field, _, T} <- Fs0],
check_type({type, nowarn(), product, Types}, St2)
@@ -2627,7 +2703,7 @@ normalise_fields(Fs) ->
%% Check if a record exists. Set State.
exist_record(Line, Name, St) ->
- case dict:is_key(Name, St#lint.records) of
+ case is_map_key(Name, St#lint.records) of
true -> used_record(Name, St);
false -> add_error(Line, {undefined_record,Name}, St)
end.
@@ -2644,13 +2720,13 @@ exist_record(Line, Name, St) ->
%% {UpdatedVarTable,State}
check_record(Line, Name, St, CheckFun) ->
- case dict:find(Name, St#lint.records) of
+ case maps:find(Name, St#lint.records) of
{ok,{_Line,Fields}} -> CheckFun(Fields, used_record(Name, St));
error -> {[],add_error(Line, {undefined_record,Name}, St)}
end.
used_record(Name, #lint{usage=Usage}=St) ->
- UsedRecs = sets:add_element(Name, Usage#usage.used_records),
+ UsedRecs = gb_sets:add_element(Name, Usage#usage.used_records),
St#lint{usage = Usage#usage{used_records=UsedRecs}}.
%%% Record check functions.
@@ -2678,9 +2754,12 @@ check_field({record_field,Lf,{atom,La,F},Val}, Name, Fields,
error -> {[],add_error(La, {undefined_field,Name,F}, St)}
end}
end;
-check_field({record_field,_Lf,{var,_La,'_'},Val}, _Name, _Fields,
+check_field({record_field,_Lf,{var,La,'_'=F},Val}, _Name, _Fields,
Vt, St, Sfs, CheckFun) ->
- {Sfs,CheckFun(Val, Vt, St)};
+ case member(F, Sfs) of
+ true -> {Sfs,{[],add_error(La, bad_multi_field_init, St)}};
+ false -> {[F|Sfs],CheckFun(Val, Vt, St)}
+ end;
check_field({record_field,_Lf,{var,La,V},_Val}, Name, _Fields,
Vt, St, Sfs, _CheckFun) ->
{Sfs,{Vt,add_error(La, {field_name_is_variable,Name,V}, St)}}.
@@ -2791,7 +2870,7 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
Info = #typeinfo{attr = Attr, line = Line},
StoreType =
fun(St) ->
- NewDefs = dict:store(TypePair, Info, TypeDefs),
+ NewDefs = maps:put(TypePair, Info, TypeDefs),
CheckType = {type, nowarn(), product, [ProtoType|Args]},
check_type(CheckType, St#lint{types=NewDefs})
end,
@@ -2811,7 +2890,7 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) ->
end
end;
false ->
- case dict:is_key(TypePair, TypeDefs) of
+ case is_map_key(TypePair, TypeDefs) of
true ->
add_error(Line, {redefine_type, TypePair}, St0);
false ->
@@ -2833,8 +2912,8 @@ is_underspecified({type,_,any,[]}, 0) -> true;
is_underspecified(_ProtType, _Arity) -> false.
check_type(Types, St) ->
- {SeenVars, St1} = check_type(Types, dict:new(), St),
- dict:fold(fun(Var, {seen_once, Line}, AccSt) ->
+ {SeenVars, St1} = check_type(Types, maps:new(), St),
+ maps:fold(fun(Var, {seen_once, Line}, AccSt) ->
case atom_to_list(Var) of
"_"++_ -> AccSt;
_ -> add_error(Line, {singleton_typevar, Var}, AccSt)
@@ -2862,10 +2941,10 @@ check_type({atom, _L, _}, SeenVars, St) -> {SeenVars, St};
check_type({var, _L, '_'}, SeenVars, St) -> {SeenVars, St};
check_type({var, L, Name}, SeenVars, St) ->
NewSeenVars =
- case dict:find(Name, SeenVars) of
- {ok, {seen_once, _}} -> dict:store(Name, seen_multiple, SeenVars);
+ case maps:find(Name, SeenVars) of
+ {ok, {seen_once, _}} -> maps:put(Name, seen_multiple, SeenVars);
{ok, seen_multiple} -> SeenVars;
- error -> dict:store(Name, {seen_once, L}, SeenVars)
+ error -> maps:put(Name, {seen_once, L}, SeenVars)
end,
{NewSeenVars, St};
check_type({type, L, bool, []}, SeenVars, St) ->
@@ -2924,7 +3003,7 @@ check_type({type, La, TypeName, Args}, SeenVars, St) ->
andalso obsolete_builtin_type(TypePair)),
St1 = case Obsolete of
{deprecated, Repl, _} when element(1, Repl) =/= Module ->
- case dict:find(TypePair, Types) of
+ case maps:find(TypePair, Types) of
{ok, _} ->
used_type(TypePair, La, St);
error ->
@@ -2953,7 +3032,7 @@ check_type(I, SeenVars, St) ->
end.
check_record_types(Line, Name, Fields, SeenVars, St) ->
- case dict:find(Name, St#lint.records) of
+ case maps:find(Name, St#lint.records) of
{ok,{_L,DefFields}} ->
case lists:all(fun({type, _, field_type, _}) -> true;
(_) -> false
@@ -2988,7 +3067,7 @@ check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->
used_type(TypePair, L, #lint{usage = Usage, file = File} = St) ->
OldUsed = Usage#usage.used_types,
- UsedTypes = dict:store(TypePair, erl_anno:set_file(File, L), OldUsed),
+ UsedTypes = maps:put(TypePair, erl_anno:set_file(File, L), OldUsed),
St#lint{usage=Usage#usage{used_types=UsedTypes}}.
is_default_type({Name, NumberOfTypeVariables}) ->
@@ -3012,8 +3091,8 @@ spec_decl(Line, MFA0, TypeSpecs, St00 = #lint{specs = Specs, module = Mod}) ->
{_M, _F, Arity} -> MFA0
end,
St0 = check_module_name(element(1, MFA), Line, St00),
- St1 = St0#lint{specs = dict:store(MFA, Line, Specs)},
- case dict:is_key(MFA, Specs) of
+ St1 = St0#lint{specs = maps:put(MFA, Line, Specs)},
+ case is_map_key(MFA, Specs) of
true -> add_error(Line, {redefine_spec, MFA0}, St1);
false ->
case MFA of
@@ -3034,8 +3113,8 @@ callback_decl(Line, MFA0, TypeSpecs,
add_error(Line, {bad_callback, MFA0}, St1);
{F, Arity} ->
MFA = {Mod, F, Arity},
- St1 = St0#lint{callbacks = dict:store(MFA, Line, Callbacks)},
- case dict:is_key(MFA, Callbacks) of
+ St1 = St0#lint{callbacks = maps:put(MFA, Line, Callbacks)},
+ case is_map_key(MFA, Callbacks) of
true -> add_error(Line, {redefine_callback, MFA0}, St1);
false -> check_specs(TypeSpecs, callback_wrong_arity,
Arity, St1)
@@ -3058,8 +3137,8 @@ optional_cbs(_Line, [], St) ->
optional_cbs(Line, [{F,A}|FAs], St0) ->
#lint{optional_callbacks = OptionalCbs, module = Mod} = St0,
MFA = {Mod, F, A},
- St1 = St0#lint{optional_callbacks = dict:store(MFA, Line, OptionalCbs)},
- St2 = case dict:is_key(MFA, OptionalCbs) of
+ St1 = St0#lint{optional_callbacks = maps:put(MFA, Line, OptionalCbs)},
+ St2 = case is_map_key(MFA, OptionalCbs) of
true ->
add_error(Line, {redefine_optional_callback, {F,A}}, St1);
false ->
@@ -3119,7 +3198,7 @@ check_specs_without_function(#lint{module=Mod,defined=Funcs,specs=Specs}=St) ->
end;
({_M, _F, _A}, _Line, AccSt) -> AccSt
end,
- dict:fold(Fun, St, Specs).
+ maps:fold(Fun, St, Specs).
%% This generates warnings for functions without specs; if the user has
%% specified both options, we do not generate the same warnings twice.
@@ -3137,7 +3216,7 @@ check_functions_without_spec(Forms, St0) ->
end.
add_missing_spec_warnings(Forms, St0, Type) ->
- Specs = [{F,A} || {_M,F,A} <- dict:fetch_keys(St0#lint.specs)],
+ Specs = [{F,A} || {_M,F,A} <- maps:keys(St0#lint.specs)],
Warns = %% functions + line numbers for which we should warn
case Type of
all ->
@@ -3163,7 +3242,7 @@ check_unused_types_1(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
case [File || {attribute,_L,file,{File,_Line}} <- Forms] of
[FirstFile|_] ->
D = Usage#usage.used_types,
- L = gb_sets:to_list(ExpTs) ++ dict:fetch_keys(D),
+ L = gb_sets:to_list(ExpTs) ++ maps:keys(D),
UsedTypes = gb_sets:from_list(L),
FoldFun =
fun({{record, _}=_Type, 0}, _, AccSt) ->
@@ -3182,7 +3261,7 @@ check_unused_types_1(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
AccSt
end
end,
- dict:fold(FoldFun, St, Ts);
+ maps:fold(FoldFun, St, Ts);
[] ->
St
end.
@@ -3200,7 +3279,7 @@ check_local_opaque_types(St) ->
add_warning(FileLine, Warn, AccSt)
end
end,
- dict:fold(FoldFun, St, Ts).
+ maps:fold(FoldFun, St, Ts).
check_dialyzer_attribute(Forms, St0) ->
Vals = [{L,V} ||
@@ -3444,7 +3523,7 @@ handle_generator(P,E,Vt,Uvt,St0) ->
handle_bitstring_gen_pat({bin,_,Segments=[_|_]},St) ->
case lists:last(Segments) of
- {bin_element,Line,{var,_,_},default,Flags} when is_list(Flags) ->
+ {bin_element,Line,_,default,Flags} when is_list(Flags) ->
case member(binary, Flags) orelse member(bytes, Flags)
orelse member(bits, Flags) orelse member(bitstring, Flags) of
true ->
@@ -3590,7 +3669,13 @@ pat_binsize_var(V, Line, Vt, Bvt, St) ->
%% exported vars are probably safe, warn only if warn_export_vars is
%% set.
-expr_var(V, Line, Vt, St) ->
+expr_var(V, Line, Vt, #lint{bvt=none}=St) ->
+ do_expr_var(V, Line, Vt, St);
+expr_var(V, Line, Vt0, #lint{bvt=Bvt0}=St0) when is_list(Bvt0) ->
+ {Vt,Bvt,St} = pat_binsize_var(V, Line, Vt0, Bvt0, St0),
+ {Vt,St#lint{bvt=vtmerge(Bvt0, Bvt)}}.
+
+do_expr_var(V, Line, Vt, St) ->
case orddict:find(V, Vt) of
{ok,{bound,_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],St};
@@ -3775,7 +3860,29 @@ has_wildcard_field([]) -> false.
check_remote_function(Line, M, F, As, St0) ->
St1 = deprecated_function(Line, M, F, As, St0),
St2 = check_qlc_hrl(Line, M, F, As, St1),
- format_function(Line, M, F, As, St2).
+ St3 = check_load_nif(Line, M, F, As, St2),
+ format_function(Line, M, F, As, St3).
+
+%% check_load_nif(Line, ModName, FuncName, [Arg], State) -> State
+%% Add warning if erlang:load_nif/2 is called when any kind of inlining has
+%% been enabled.
+check_load_nif(Line, erlang, load_nif, [_, _], St) ->
+ case is_warn_enabled(nif_inline, St) of
+ true -> check_nif_inline(Line, St);
+ false -> St
+ end;
+check_load_nif(_Line, _ModName, _FuncName, _Args, St) ->
+ St.
+
+check_nif_inline(Line, St) ->
+ case any(fun is_inline_opt/1, St#lint.compile) of
+ true -> add_warning(Line, nif_inline, St);
+ false -> St
+ end.
+
+is_inline_opt({inline, [_|_]=_FAs}) -> true;
+is_inline_opt(inline) -> true;
+is_inline_opt(_) -> false.
%% check_qlc_hrl(Line, ModName, FuncName, [Arg], State) -> State
%% Add warning if qlc:q/1,2 has been called but qlc.hrl has not
@@ -3846,8 +3953,8 @@ deprecated_type(L, M, N, As, St) ->
false ->
St
end;
- {removed, Replacement, Rel} ->
- add_warning(L, {removed_type, {M,N,NAs}, Replacement, Rel}, St);
+ {removed, String} ->
+ add_warning(L, {removed_type, {M,N,NAs}, String}, St);
no ->
St
end.
@@ -4122,3 +4229,13 @@ no_guard_bif_clash(St,{F,A}) ->
is_imported_from_erlang(St#lint.imports,{F,A})
)
).
+
+%% maps_prepend(Key, Value, Map) -> Map.
+
+maps_prepend(Key, Value, Map) ->
+ case maps:find(Key, Map) of
+ {ok, Values} ->
+ maps:put(Key, [Value|Values], Map);
+ error ->
+ maps:put(Key, [Value], Map)
+ end.
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 739f786321..dd7a2c2cc1 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -608,6 +608,15 @@ Erlang code.
-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0,
abstract_type/0, form_info/0, error_info/0]).
+%% The following types are exported because they are used by syntax_tools
+-export_type([af_binelement/1, af_generator/0, af_remote_function/0]).
+%% The following type is used by PropEr
+-export_type([af_field_decl/0]).
+
+%% Removed functions
+-removed([{set_line,2,"use erl_anno:set_line/2"},
+ {get_attributes,1,"erl_anno:{column,line,location,text}/1 instead"},
+ {get_attribute,2,"erl_anno:{column,line,location,text}/1 instead"}]).
%% Start of Abstract Format
@@ -637,7 +646,7 @@ Erlang code.
-type af_export() :: {'attribute', anno(), 'export', af_fa_list()}.
--type af_import() :: {'attribute', anno(), 'import', af_fa_list()}.
+-type af_import() :: {'attribute', anno(), 'import', {module(), af_fa_list()}}.
-type af_fa_list() :: [{function_name(), arity()}].
@@ -1455,7 +1464,19 @@ abstract(List, A, E) when is_list(List) ->
abstract(Tuple, A, E) when is_tuple(Tuple) ->
{tuple,A,abstract_tuple_list(tuple_to_list(Tuple), A, E)};
abstract(Map, A, E) when is_map(Map) ->
- {map,A,abstract_map_fields(maps:to_list(Map),A,E)}.
+ {map,A,abstract_map_fields(maps:to_list(Map),A,E)};
+abstract(Fun, A, E) when is_function(Fun) ->
+ case erlang:fun_info(Fun, type) of
+ {type, external} ->
+ Info = erlang:fun_info(Fun),
+ {module, M} = lists:keyfind(module, 1, Info),
+ {name, F} = lists:keyfind(name, 1, Info),
+ {arity, Arity} = lists:keyfind(arity, 1, Info),
+ {'fun', A, {function,
+ abstract(M, A, E),
+ abstract(F, A, E),
+ abstract(Arity, A, E)}}
+ end.
abstract_list([H|T], String, A, E) ->
case is_integer(H) andalso H >= 0 andalso E(H) of
@@ -1514,7 +1535,13 @@ tokens({cons,A,Head,Tail}, More) ->
tokens({tuple,A,[]}, More) ->
[{'{',A},{'}',A}|More];
tokens({tuple,A,[E|Es]}, More) ->
- [{'{',A}|tokens(E, tokens_tuple(Es, ?anno(E), More))].
+ [{'{',A}|tokens(E, tokens_tuple(Es, ?anno(E), More))];
+tokens({map,A,[]}, More) ->
+ [{'#',A},{'{',A},{'}',A}|More];
+tokens({map,A,[P|Ps]}, More) ->
+ [{'#',A},{'{',A}|tokens(P, tokens_tuple(Ps, ?anno(P), More))];
+tokens({map_field_assoc,A,K,V}, More) ->
+ tokens(K, [{'=>',A}|tokens(V, More)]).
tokens_tail({cons,A,Head,Tail}, More) ->
[{',',A}|tokens(Head, tokens_tail(Tail, More))];
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index daa172af50..651c601bb0 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -31,7 +31,8 @@
-import(erl_parse, [inop_prec/1,preop_prec/1,func_prec/0,max_prec/0,
type_inop_prec/1, type_preop_prec/1]).
--define(MAXLINE, 72).
+-define(DEFAULT_LINEWIDTH, 72).
+-define(DEFAULT_INDENT, 4).
-type(hook_function() :: none
| fun((Expr :: erl_parse:abstract_expr(),
@@ -42,10 +43,13 @@
-type(option() :: {hook, hook_function()}
| {encoding, latin1 | unicode | utf8}
- | {quote_singleton_atom_types, boolean()}).
+ | {quote_singleton_atom_types, boolean()}
+ | {linewidth, pos_integer()}
+ | {indent, pos_integer()}).
-type(options() :: hook_function() | [option()]).
--record(pp, {value_fun, singleton_atom_type_fun, string_fun, char_fun}).
+-record(pp, {value_fun, singleton_atom_type_fun, string_fun, char_fun,
+ linewidth=?DEFAULT_LINEWIDTH, indent=?DEFAULT_INDENT}).
-record(options, {hook, encoding, opts}).
@@ -208,10 +212,14 @@ options(Hook) ->
state(Options) when is_list(Options) ->
Quote = proplists:get_bool(quote_singleton_atom_types, Options),
- case encoding(Options) of
- latin1 -> latin1_state(Quote);
- unicode -> unicode_state(Quote)
- end;
+ State =
+ case encoding(Options) of
+ latin1 -> latin1_state(Quote);
+ unicode -> unicode_state(Quote)
+ end,
+ Indent = proplists:get_value(indent, Options, ?DEFAULT_INDENT),
+ LineWidth = proplists:get_value(linewidth, Options, ?DEFAULT_LINEWIDTH),
+ State#pp{indent=Indent, linewidth=LineWidth};
state(_Hook) ->
latin1_state(false).
@@ -552,8 +560,6 @@ lexpr({bc,_,E,Qs}, _Prec, Opts) ->
%% {list,[{step,'<<',Lcl},'>>']};
lexpr({tuple,_,Elts}, _, Opts) ->
tuple(Elts, Opts);
-%%lexpr({struct,_,Tag,Elts}, _, Opts) ->
-%% {first,format("~w", [Tag]),tuple(Elts, Opts)};
lexpr({record_index, _, Name, F}, Prec, Opts) ->
{P,R} = preop_prec('#'),
Nl = record_name(Name),
@@ -644,8 +650,10 @@ lexpr({named_fun,_,Name,Cs,Extra}, _Prec, Opts) ->
{reserved,'end'}]}};
lexpr({call,_,{remote,_,{atom,_,M},{atom,_,F}=N}=Name,Args}, Prec, Opts) ->
case erl_internal:bif(M, F, length(Args)) of
- true ->
+ true when F =/= float ->
call(N, Args, Prec, Opts);
+ true ->
+ call(Name, Args, Prec, Opts);
false ->
call(Name, Args, Prec, Opts)
end;
@@ -1020,7 +1028,7 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT, PP) ->
true ->
0
end,
- case same_line(I0, Sizes, NSepChars) of
+ case same_line(I0, Sizes, NSepChars, PP) of
{yes,Size} ->
Chars = if
NSepChars > 0 -> insert_sep(CharsL, $\s);
@@ -1028,9 +1036,9 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT, PP) ->
end,
{BCharsL++Chars,Size};
no ->
- CharsList = handle_step(CharsSizeL, I, ST),
+ CharsList = handle_step(CharsSizeL, I, ST, PP),
{LChars, LSize} =
- maybe_newlines(CharsList, LItems, I, NSepChars, ST),
+ maybe_newlines(CharsList, LItems, I, NSepChars, ST, PP),
{[BCharsL,LChars],nsz(LSize, I0)}
end;
f({force_nl,_ExtraInfoItem,Item}, I, ST, WT, PP) when I < 0 ->
@@ -1047,7 +1055,7 @@ f({prefer_nl,Sep,LItems}, I0, ST, WT, PP) ->
Sizes =:= [] ->
{[], 0};
true ->
- {insert_newlines(CharsSize2L, I0, ST),
+ {insert_newlines(CharsSize2L, I0, ST, PP),
nsz(lists:last(Sizes), I0)}
end;
f({value,V}, I, ST, WT, PP) ->
@@ -1071,8 +1079,6 @@ f({ehook,HookExpr,Precedence,{Mod,Func,Eas}=ModFuncEas}, I, _ST, _WT, _PP) ->
f(WordName, _I, _ST, WT, _PP) when is_atom(WordName) ->
word(WordName, WT).
--define(IND, 4).
-
%% fl(ListItems, I0, ST, WT) -> [[CharsSize1,CharsSize2]]
%% ListItems = [{Item,Items}|Item]
fl([], _Sep, I0, After, ST, WT, PP) ->
@@ -1080,15 +1086,15 @@ fl([], _Sep, I0, After, ST, WT, PP) ->
fl(CItems, Sep0, I0, After, ST, WT, PP) ->
F = fun({step,Item1,Item2}, S) ->
[f(Item1, I0, ST, WT, PP),
- f([Item2,S], incr(I0, ?IND), ST, WT, PP)];
+ f([Item2,S], incr(I0, PP#pp.indent), ST, WT, PP)];
({cstep,Item1,Item2}, S) ->
{_,Sz1} = CharSize1 = f(Item1, I0, ST, WT, PP),
if
- is_integer(Sz1), Sz1 < ?IND ->
+ is_integer(Sz1), Sz1 < PP#pp.indent ->
Item2p = [leaf("\s"),Item2,S],
[consecutive(Item2p, CharSize1, I0, ST, WT, PP),{[],0}];
true ->
- [CharSize1,f([Item2,S], incr(I0, ?IND), ST, WT, PP)]
+ [CharSize1,f([Item2,S], incr(I0, PP#pp.indent), ST, WT, PP)]
end;
({reserved,Word}, S) ->
[f([Word,S], I0, ST, WT, PP),{[],0}];
@@ -1127,58 +1133,58 @@ unz1(CharSizes) ->
nonzero(CharSizes) ->
lists:filter(fun({_,Sz}) -> Sz =/= 0 end, CharSizes).
-maybe_newlines([{Chars,Size}], [], _I, _NSepChars, _ST) ->
+maybe_newlines([{Chars,Size}], [], _I, _NSepChars, _ST, _PP) ->
{Chars,Size};
-maybe_newlines(CharsSizeList, Items, I, NSepChars, ST) when I >= 0 ->
- maybe_sep(CharsSizeList, Items, I, NSepChars, nl_indent(I, ST)).
+maybe_newlines(CharsSizeList, Items, I, NSepChars, ST, PP) when I >= 0 ->
+ maybe_sep(CharsSizeList, Items, I, NSepChars, nl_indent(I, ST), PP).
-maybe_sep([{Chars1,Size1}|CharsSizeL], [Item|Items], I0, NSepChars, Sep) ->
+maybe_sep([{Chars1,Size1}|CharsSizeL], [Item|Items], I0, NSepChars, Sep, PP) ->
I1 = case classify_item(Item) of
atomic ->
I0 + Size1;
_ ->
- ?MAXLINE+1
+ PP#pp.linewidth+1
end,
- maybe_sep1(CharsSizeL, Items, I0, I1, Sep, NSepChars, Size1, [Chars1]).
+ maybe_sep1(CharsSizeL, Items, I0, I1, Sep, NSepChars, Size1, [Chars1], PP).
maybe_sep1([{Chars,Size}|CharsSizeL], [Item|Items],
- I0, I, Sep, NSepChars, Sz0, A) ->
+ I0, I, Sep, NSepChars, Sz0, A, PP) ->
case classify_item(Item) of
atomic when is_integer(Size) ->
Size1 = Size + 1,
I1 = I + Size1,
if
- I1 =< ?MAXLINE ->
+ I1 =< PP#pp.linewidth ->
A1 = if
NSepChars > 0 -> [Chars,$\s|A];
true -> [Chars|A]
end,
maybe_sep1(CharsSizeL, Items, I0, I1, Sep, NSepChars,
- Sz0 + Size1, A1);
+ Sz0 + Size1, A1, PP);
true ->
A1 = [Chars,Sep|A],
maybe_sep1(CharsSizeL, Items, I0, I0 + Size, Sep,
- NSepChars, Size1, A1)
+ NSepChars, Size1, A1, PP)
end;
_ ->
A1 = [Chars,Sep|A],
- maybe_sep1(CharsSizeL, Items, I0, ?MAXLINE+1, Sep, NSepChars,
- 0, A1)
+ maybe_sep1(CharsSizeL, Items, I0, PP#pp.linewidth+1, Sep, NSepChars,
+ 0, A1, PP)
end;
-maybe_sep1(_CharsSizeL, _Items, _Io, _I, _Sep, _NSepChars, Sz, A) ->
+maybe_sep1(_CharsSizeL, _Items, _Io, _I, _Sep, _NSepChars, Sz, A, _PP) ->
{lists:reverse(A), Sz}.
-insert_newlines(CharsSizesL, I, ST) when I >= 0 ->
- {CharsL, _} = unz1(handle_step(CharsSizesL, I, ST)),
+insert_newlines(CharsSizesL, I, ST, PP) when I >= 0 ->
+ {CharsL, _} = unz1(handle_step(CharsSizesL, I, ST, PP)),
insert_nl(CharsL, I, ST).
-handle_step(CharsSizesL, I, ST) ->
+handle_step(CharsSizesL, I, ST, PP) ->
map(fun([{_C1,0},{_C2,0}]) ->
{[], 0};
([{C1,Sz1},{_C2,0}]) ->
{C1, Sz1};
([{C1,Sz1},{C2,Sz2}]) when Sz2 > 0 ->
- {insert_nl([C1,C2], I+?IND, ST),line_size([Sz1,Sz2])}
+ {insert_nl([C1,C2], I+PP#pp.indent, ST),line_size([Sz1,Sz2])}
end, CharsSizesL).
insert_nl(CharsL, I, ST) ->
@@ -1198,10 +1204,10 @@ classify_item(Atom) when is_atom(Atom) -> atomic;
classify_item({leaf, _, _}) -> atomic;
classify_item(_) -> complex.
-same_line(I0, SizeL, NSepChars) ->
+same_line(I0, SizeL, NSepChars, PP) ->
try
Size = lists:sum(SizeL) + NSepChars,
- true = incr(I0, Size) =< ?MAXLINE,
+ true = incr(I0, Size) =< PP#pp.linewidth,
{yes,Size}
catch _:_ ->
no
@@ -1269,7 +1275,7 @@ write_a_char(C, PP) ->
write_a_string(S, I, PP) when I < 0; S =:= [] ->
flat_leaf(write_string(S, PP));
write_a_string(S, I, PP) ->
- Len = erlang:max(?MAXLINE-I, ?MIN_SUBSTRING),
+ Len = erlang:max(PP#pp.linewidth-I, ?MIN_SUBSTRING),
{list,write_a_string(S, Len, Len, PP)}.
write_a_string([], _N, _Len, _PP) ->
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 4774c4bf19..0854e15177 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -66,6 +66,17 @@
token/0,
tokens_result/0]).
+%% Removed functions and types
+-removed([{set_attribute,3,"use erl_anno:set_line/2 instead"},
+ {attributes_info,'_',
+ "erl_anno:{column,line,location,text}/1 instead"},
+ {token_info,'_',
+ "erl_scan:{category,column,line,location,symbol,text}/1 instead"}]).
+
+-removed_type([{column,0,"use erl_anno:column() instead"},
+ {line,0,"use erl_anno:line() instead"},
+ {location,0,"use erl_anno:location() instead"}]).
+
%%%
%%% Defines and type definitions
%%%
@@ -249,7 +260,7 @@ string_thing(_) -> "string".
-define(WHITE_SPACE(C),
is_integer(C) andalso
(C >= $\000 andalso C =< $\s orelse C >= $\200 andalso C =< $\240)).
--define(DIGIT(C), C >= $0, C =< $9).
+-define(DIGIT(C), C >= $0 andalso C =< $9).
-define(CHAR(C), is_integer(C), C >= 0).
-define(UNICODE(C),
is_integer(C) andalso
@@ -379,7 +390,7 @@ scan1([$\%|Cs], St, Line, Col, Toks) when not St#erl_scan.comment ->
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]);
+ scan_number(Cs, St, Line, Col, Toks, [C], no_underscore);
scan1("..."++Cs, St, Line, Col, Toks) ->
tok2(Cs, St, Line, Col, Toks, "...", '...', 3);
scan1(".."=Cs, _St, Line, Col, Toks) ->
@@ -938,27 +949,35 @@ escape_char($s) -> $\s; % \s = SPC
escape_char($d) -> $\d; % \d = DEL
escape_char(C) -> C.
-scan_number([C|Cs], St, Line, Col, Toks, Ncs) when ?DIGIT(C) ->
- scan_number(Cs, St, Line, Col, Toks, [C|Ncs]);
-scan_number([$.,C|Cs], St, Line, Col, Toks, Ncs) when ?DIGIT(C) ->
- scan_fraction(Cs, St, Line, Col, Toks, [C,$.|Ncs]);
-scan_number([$.]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_number/6}};
-scan_number([$#|Cs]=Cs0, St, Line, Col, Toks, Ncs0) ->
+scan_number(Cs, St, Line, Col, Toks, {Ncs, Us}) ->
+ scan_number(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_number([C|Cs], St, Line, Col, Toks, Ncs, Us) when ?DIGIT(C) ->
+ scan_number(Cs, St, Line, Col, Toks, [C|Ncs], Us);
+scan_number([$_,Next|Cs], St, Line, Col, Toks, [Prev|_]=Ncs, _Us) when
+ ?DIGIT(Next) andalso ?DIGIT(Prev) ->
+ scan_number(Cs, St, Line, Col, Toks, [Next,$_|Ncs], with_underscore);
+scan_number([$_]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_number/6}};
+scan_number([$.,C|Cs], St, Line, Col, Toks, Ncs, Us) when ?DIGIT(C) ->
+ scan_fraction(Cs, St, Line, Col, Toks, [C,$.|Ncs], Us);
+scan_number([$.]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_number/6}};
+scan_number([$#|Cs]=Cs0, St, Line, Col, Toks, Ncs0, Us) ->
Ncs = lists:reverse(Ncs0),
- case catch list_to_integer(Ncs) of
+ case catch list_to_integer(remove_digit_separators(Ncs, Us)) of
B when B >= 2, B =< 1+$Z-$A+10 ->
Bcs = Ncs++[$#],
- scan_based_int(Cs, St, Line, Col, Toks, {B,[],Bcs});
+ scan_based_int(Cs, St, Line, Col, Toks, B, [], Bcs, no_underscore);
B ->
Len = length(Ncs),
scan_error({base,B}, Line, Col, Line, incr_column(Col, Len), Cs0)
end;
-scan_number([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_number/6}};
-scan_number(Cs, St, Line, Col, Toks, Ncs0) ->
+scan_number([]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_number/6}};
+scan_number(Cs, St, Line, Col, Toks, Ncs0, Us) ->
Ncs = lists:reverse(Ncs0),
- case catch list_to_integer(Ncs) of
+ case catch list_to_integer(remove_digit_separators(Ncs, Us)) of
N when is_integer(N) ->
tok3(Cs, St, Line, Col, Toks, integer, Ncs, N);
_ ->
@@ -966,20 +985,33 @@ scan_number(Cs, St, Line, Col, Toks, Ncs0) ->
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});
-scan_based_int([C|Cs], St, Line, Col, Toks, {B,Ncs,Bcs})
- when C >= $A, B > 10, C < $A+B-10 ->
- scan_based_int(Cs, St, Line, Col, Toks, {B,[C|Ncs],Bcs});
-scan_based_int([C|Cs], St, Line, Col, Toks, {B,Ncs,Bcs})
- when C >= $a, B > 10, C < $a+B-10 ->
- scan_based_int(Cs, St, Line, Col, Toks, {B,[C|Ncs],Bcs});
-scan_based_int([]=Cs, _St, Line, Col, Toks, State) ->
- {more,{Cs,Col,Toks,Line,State,fun scan_based_int/6}};
-scan_based_int(Cs, St, Line, Col, Toks, {B,Ncs0,Bcs}) ->
+remove_digit_separators(Number, no_underscore) ->
+ Number;
+remove_digit_separators(Number, with_underscore) ->
+ [C || C <- Number, C =/= $_].
+
+-define(BASED_DIGIT(C, B),
+ ((?DIGIT(C) andalso C < $0 + B)
+ orelse (C >= $A andalso B > 10 andalso C < $A + B - 10)
+ orelse (C >= $a andalso B > 10 andalso C < $a + B - 10))).
+
+scan_based_int(Cs, St, Line, Col, Toks, {B,NCs,BCs,Us}) ->
+ scan_based_int(Cs, St, Line, Col, Toks, B, NCs, BCs, Us).
+
+scan_based_int([C|Cs], St, Line, Col, Toks, B, Ncs, Bcs, Us) when
+ ?BASED_DIGIT(C, B) ->
+ scan_based_int(Cs, St, Line, Col, Toks, B, [C|Ncs], Bcs, Us);
+scan_based_int([$_,Next|Cs], St, Line, Col, Toks, B, [Prev|_]=Ncs, Bcs, _Us)
+ when ?BASED_DIGIT(Next, B) andalso ?BASED_DIGIT(Prev, B) ->
+ scan_based_int(Cs, St, Line, Col, Toks, B, [Next,$_|Ncs], Bcs,
+ with_underscore);
+scan_based_int([$_]=Cs, _St, Line, Col, Toks, B, NCs, BCs, Us) ->
+ {more,{Cs,Col,Toks,Line,{B,NCs,BCs,Us},fun scan_based_int/6}};
+scan_based_int([]=Cs, _St, Line, Col, Toks, B, NCs, BCs, Us) ->
+ {more,{Cs,Col,Toks,Line,{B,NCs,BCs,Us},fun scan_based_int/6}};
+scan_based_int(Cs, St, Line, Col, Toks, B, Ncs0, Bcs, Us) ->
Ncs = lists:reverse(Ncs0),
- case catch erlang:list_to_integer(Ncs, B) of
+ case catch erlang:list_to_integer(remove_digit_separators(Ncs, Us), B) of
N when is_integer(N) ->
tok3(Cs, St, Line, Col, Toks, integer, Bcs++Ncs, N);
_ ->
@@ -988,32 +1020,52 @@ scan_based_int(Cs, St, Line, Col, Toks, {B,Ncs0,Bcs}) ->
scan_error({illegal,integer}, Line, Col, Line, Ncol, Cs)
end.
-scan_fraction([C|Cs], St, Line, Col, Toks, Ncs) when ?DIGIT(C) ->
- scan_fraction(Cs, St, Line, Col, Toks, [C|Ncs]);
-scan_fraction([E|Cs], St, Line, Col, Toks, Ncs) when E =:= $e; E =:= $E ->
- scan_exponent_sign(Cs, St, Line, Col, Toks, [E|Ncs]);
-scan_fraction([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_fraction/6}};
-scan_fraction(Cs, St, Line, Col, Toks, Ncs) ->
- float_end(Cs, St, Line, Col, Toks, Ncs).
-
-scan_exponent_sign([C|Cs], St, Line, Col, Toks, Ncs) when C =:= $+; C =:= $- ->
- scan_exponent(Cs, St, Line, Col, Toks, [C|Ncs]);
-scan_exponent_sign([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_exponent_sign/6}};
-scan_exponent_sign(Cs, St, Line, Col, Toks, Ncs) ->
- scan_exponent(Cs, St, Line, Col, Toks, Ncs).
-
-scan_exponent([C|Cs], St, Line, Col, Toks, Ncs) when ?DIGIT(C) ->
- scan_exponent(Cs, St, Line, Col, Toks, [C|Ncs]);
-scan_exponent([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_exponent/6}};
-scan_exponent(Cs, St, Line, Col, Toks, Ncs) ->
- float_end(Cs, St, Line, Col, Toks, Ncs).
-
-float_end(Cs, St, Line, Col, Toks, Ncs0) ->
+scan_fraction(Cs, St, Line, Col, Toks, {Ncs,Us}) ->
+ scan_fraction(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_fraction([C|Cs], St, Line, Col, Toks, Ncs, Us) when ?DIGIT(C) ->
+ scan_fraction(Cs, St, Line, Col, Toks, [C|Ncs], Us);
+scan_fraction([$_,Next|Cs], St, Line, Col, Toks, [Prev|_]=Ncs, _Us) when
+ ?DIGIT(Next) andalso ?DIGIT(Prev) ->
+ scan_fraction(Cs, St, Line, Col, Toks, [Next,$_|Ncs], with_underscore);
+scan_fraction([$_]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_fraction/6}};
+scan_fraction([E|Cs], St, Line, Col, Toks, Ncs, Us) when E =:= $e; E =:= $E ->
+ scan_exponent_sign(Cs, St, Line, Col, Toks, [E|Ncs], Us);
+scan_fraction([]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_fraction/6}};
+scan_fraction(Cs, St, Line, Col, Toks, Ncs, Us) ->
+ float_end(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_exponent_sign(Cs, St, Line, Col, Toks, {Ncs, Us}) ->
+ scan_exponent_sign(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_exponent_sign([C|Cs], St, Line, Col, Toks, Ncs, Us) when
+ C =:= $+; C =:= $- ->
+ scan_exponent(Cs, St, Line, Col, Toks, [C|Ncs], Us);
+scan_exponent_sign([]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_exponent_sign/6}};
+scan_exponent_sign(Cs, St, Line, Col, Toks, Ncs, Us) ->
+ scan_exponent(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_exponent(Cs, St, Line, Col, Toks, {Ncs, Us}) ->
+ scan_exponent(Cs, St, Line, Col, Toks, Ncs, Us).
+
+scan_exponent([C|Cs], St, Line, Col, Toks, Ncs, Us) when ?DIGIT(C) ->
+ scan_exponent(Cs, St, Line, Col, Toks, [C|Ncs], Us);
+scan_exponent([$_,Next|Cs], St, Line, Col, Toks, [Prev|_]=Ncs, _) when
+ ?DIGIT(Next) andalso ?DIGIT(Prev) ->
+ scan_exponent(Cs, St, Line, Col, Toks, [Next,$_|Ncs], with_underscore);
+scan_exponent([$_]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_exponent/6}};
+scan_exponent([]=Cs, _St, Line, Col, Toks, Ncs, Us) ->
+ {more,{Cs,Col,Toks,Line,{Ncs,Us},fun scan_exponent/6}};
+scan_exponent(Cs, St, Line, Col, Toks, Ncs, Us) ->
+ float_end(Cs, St, Line, Col, Toks, Ncs, Us).
+
+float_end(Cs, St, Line, Col, Toks, Ncs0, Us) ->
Ncs = lists:reverse(Ncs0),
- case catch list_to_float(Ncs) of
+ case catch list_to_float(remove_digit_separators(Ncs, Us)) of
F when is_float(F) ->
tok3(Cs, St, Line, Col, Toks, float, Ncs, F);
_ ->
diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index c2c498cf0a..009e72b957 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -321,22 +321,29 @@ do_open(Name, Mode) when is_list(Mode) ->
{error, {Name, Reason}}
end.
-open1({binary,Bin}, read, _Raw, Opts) when is_binary(Bin) ->
+open1({binary,Bin}=Handle, read, _Raw, Opts) when is_binary(Bin) ->
case file:open(Bin, [ram,binary,read]) of
{ok,File} ->
_ = [ram_file:uncompress(File) || lists:member(compressed, Opts)],
{ok, #reader{handle=File,access=read,func=fun file_op/2}};
- Error ->
- Error
+ {error, Reason} ->
+ {error, {Handle, Reason}}
end;
-open1({file, Fd}, read, _Raw, _Opts) ->
- Reader = #reader{handle=Fd,access=read,func=fun file_op/2},
- case do_position(Reader, {cur, 0}) of
- {ok, Pos, Reader2} ->
- {ok, Reader2#reader{pos=Pos}};
- {error, _} = Err ->
- Err
+open1({file, Fd}=Handle, read, [raw], Opts) ->
+ case not lists:member(compressed, Opts) of
+ true ->
+ Reader = #reader{handle=Fd,access=read,func=fun file_op/2},
+ case do_position(Reader, {cur, 0}) of
+ {ok, Pos, Reader2} ->
+ {ok, Reader2#reader{pos=Pos}};
+ {error, Reason} ->
+ {error, {Handle, Reason}}
+ end;
+ false ->
+ {error, {Handle, {incompatible_option, compressed}}}
end;
+open1({file, _Fd}=Handle, read, [], _Opts) ->
+ {error, {Handle, {incompatible_option, cooked}}};
open1(Name, Access, Raw, Opts) when is_list(Name) or is_binary(Name) ->
case file:open(Name, Raw ++ [binary, Access|Opts]) of
{ok, File} ->
@@ -1637,60 +1644,18 @@ write_extracted_element(#tar_header{name=Name0}=Header, Bin, Opts) ->
make_safe_path([$/|Path], Opts) ->
make_safe_path(Path, Opts);
-make_safe_path(Path, #read_opts{cwd=Cwd}) ->
- case filename:safe_relative_path(Path) of
- unsafe ->
- throw({error,{Path,unsafe_path}});
- SafePath ->
- filename:absname(SafePath, Cwd)
+make_safe_path(Path0, #read_opts{cwd=Cwd}) ->
+ case filelib:safe_relative_path(Path0, Cwd) of
+ unsafe -> throw({error,{Path0,unsafe_path}});
+ Path -> filename:absname(Path, Cwd)
end.
-safe_link_name(#tar_header{linkname=Path}, #read_opts{cwd=Cwd}) ->
- case safe_relative_path_links(Path, Cwd) of
- unsafe ->
- throw({error,{Path,unsafe_symlink}});
- SafePath ->
- SafePath
+safe_link_name(#tar_header{linkname=Path0},#read_opts{cwd=Cwd} ) ->
+ case filelib:safe_relative_path(Path0, Cwd) of
+ unsafe -> throw({error,{Path0,unsafe_symlink}});
+ Path -> Path
end.
-safe_relative_path_links(Path, Cwd) ->
- case filename:pathtype(Path) of
- relative -> safe_relative_path_links(filename:split(Path), Cwd, [], "");
- _ -> unsafe
- end.
-
-safe_relative_path_links([Segment|Segments], Cwd, PrevSegments, Acc) ->
- AccSegment = join(Acc, Segment),
- case lists:member(AccSegment, PrevSegments) of
- true ->
- unsafe;
- false ->
- case file:read_link(join(Cwd, AccSegment)) of
- {ok, LinkPath} ->
- case filename:pathtype(LinkPath) of
- relative ->
- safe_relative_path_links(filename:split(LinkPath) ++ Segments,
- Cwd, [AccSegment|PrevSegments], Acc);
- _ ->
- unsafe
- end;
-
- {error, _} ->
- case filename:safe_relative_path(join(Acc, Segment)) of
- unsafe ->
- unsafe;
- NewAcc ->
- safe_relative_path_links(Segments, Cwd,
- [AccSegment|PrevSegments], NewAcc)
- end
- end
- end;
-safe_relative_path_links([], _Cwd, _PrevSegments, Acc) ->
- Acc.
-
-join([], Path) -> Path;
-join(Left, Right) -> filename:join(Left, Right).
-
create_regular(Name, NameInArchive, Bin, Opts) ->
case write_extracted_file(Name, Bin, Opts) of
not_written ->
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 4e9ba1cc16..0e120174fe 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -780,7 +780,7 @@ interpret(Forms, HasRecs, File, Args) ->
false -> Forms;
true -> erl_expand_records:module(Forms, [])
end,
- Dict = parse_to_dict(Forms2),
+ Dict = parse_to_map(Forms2),
ArgsA = erl_parse:abstract(Args, 0),
Anno = a0(),
Call = {call,Anno,{atom,Anno,main},[ArgsA]},
@@ -824,29 +824,29 @@ format_message(F, [{Mod,E}|Es]) ->
[M|format_message(F, Es)];
format_message(_, []) -> [].
-parse_to_dict(L) -> parse_to_dict(L, dict:new()).
-
-parse_to_dict([{function,_,Name,Arity,Clauses}|T], Dict0) ->
- Dict = dict:store({local, Name,Arity}, Clauses, Dict0),
- parse_to_dict(T, Dict);
-parse_to_dict([{attribute,_,import,{Mod,Funcs}}|T], Dict0) ->
- Dict = lists:foldl(fun(I, D) ->
- dict:store({remote,I}, Mod, D)
- end, Dict0, Funcs),
- parse_to_dict(T, Dict);
-parse_to_dict([_|T], Dict) ->
- parse_to_dict(T, Dict);
-parse_to_dict([], Dict) ->
- Dict.
+parse_to_map(L) -> parse_to_map(L, maps:new()).
+
+parse_to_map([{function,_,Name,Arity,Clauses}|T], Map0) ->
+ Map = maps:put({local, Name,Arity}, Clauses, Map0),
+ parse_to_map(T, Map);
+parse_to_map([{attribute,_,import,{Mod,Funcs}}|T], Map0) ->
+ Map = lists:foldl(fun(I, D) ->
+ maps:put({remote,I}, Mod, D)
+ end, Map0, Funcs),
+ parse_to_map(T, Map);
+parse_to_map([_|T], Map) ->
+ parse_to_map(T, Map);
+parse_to_map([], Map) ->
+ Map.
code_handler(local, [file], _, File) ->
File;
-code_handler(Name, Args, Dict, File) ->
+code_handler(Name, Args, Map, File) ->
%%io:format("code handler=~p~n",[{Name, Args}]),
Arity = length(Args),
- case dict:find({local,Name,Arity}, Dict) of
+ case maps:find({local,Name,Arity}, Map) of
{ok, Cs} ->
- LF = {value,fun(I, J) -> code_handler(I, J, Dict, File) end},
+ LF = {value,fun(I, J) -> code_handler(I, J, Map, File) end},
case erl_eval:match_clause(Cs, Args,erl_eval:new_bindings(),LF) of
{Body, Bs} ->
eval_exprs(Body, Bs, LF, none, none);
@@ -854,7 +854,7 @@ code_handler(Name, Args, Dict, File) ->
erlang:error({function_clause,[{local,Name,Args}]})
end;
error ->
- case dict:find({remote,{Name,Arity}}, Dict) of
+ case maps:find({remote,{Name,Arity}}, Map) of
{ok, Mod} ->
%% io:format("Calling:~p~n",[{Mod,Name,Args}]),
apply(Mod, Name, Args);
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 385b736b29..951a63892e 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -155,6 +155,7 @@ give_away(_, _, _) ->
Tab :: tab(),
InfoList :: [InfoTuple],
InfoTuple :: {compressed, boolean()}
+ | {decentralized_counters, boolean()}
| {heir, pid() | none}
| {id, tid()}
| {keypos, pos_integer()}
@@ -174,7 +175,7 @@ info(_) ->
-spec info(Tab, Item) -> Value | undefined when
Tab :: tab(),
- Item :: binary | compressed | fixed | heir | id | keypos | memory
+ Item :: binary | compressed | decentralized_counters | fixed | heir | id | keypos | memory
| name | named_table | node | owner | protection
| safe_fixed | safe_fixed_monotonic_time | size | stats | type
| write_concurrency | read_concurrency,
@@ -311,6 +312,7 @@ member(_, _) ->
Access :: access(),
Tweaks :: {write_concurrency, boolean()}
| {read_concurrency, boolean()}
+ | {decentralized_counters, boolean()}
| compressed,
Pos :: pos_integer(),
HeirData :: term().
diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl
index bb86a65c72..5c75320de0 100644
--- a/lib/stdlib/src/eval_bits.erl
+++ b/lib/stdlib/src/eval_bits.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -81,8 +81,15 @@ eval_field({bin_element, Line, {string, _, S}, Size0, Options0}, Bs0, Fun) ->
make_bit_type(Line, Size0, Options0),
{value,Size,Bs1} = Fun(Size1, Bs0),
Res = << <<(eval_exp_field1(C, Size, Unit,
- Type, Endian, Sign))/binary>> ||
+ Type, Endian, Sign))/bitstring>> ||
C <- S >>,
+ case S of
+ "" -> % find errors also when the string is empty
+ _ = eval_exp_field1(0, Size, Unit, Type, Endian, Sign),
+ ok;
+ _ ->
+ ok
+ end,
{Res,Bs1};
eval_field({bin_element,Line,E,Size0,Options0}, Bs0, Fun) ->
{value,V,Bs1} = Fun(E, Bs0),
@@ -119,10 +126,14 @@ eval_exp_field(Val, _Size, _Unit, utf16, big, _) ->
<<Val/big-utf16>>;
eval_exp_field(Val, _Size, _Unit, utf16, little, _) ->
<<Val/little-utf16>>;
+eval_exp_field(Val, _Size, _Unit, utf16, native, _) ->
+ <<Val/native-utf16>>;
eval_exp_field(Val, _Size, _Unit, utf32, big, _) ->
<<Val/big-utf32>>;
eval_exp_field(Val, _Size, _Unit, utf32, little, _) ->
<<Val/little-utf32>>;
+eval_exp_field(Val, _Size, _Unit, utf32, native, _) ->
+ <<Val/native-utf32>>;
eval_exp_field(Val, Size, Unit, float, little, _) ->
<<Val:(Size*Unit)/float-little>>;
eval_exp_field(Val, Size, Unit, float, native, _) ->
@@ -187,7 +198,6 @@ bin_gen_field({bin_element,Line,{string,SLine,S},Size0,Options0},
Bin0, Bs0, BBs0, Mfun, Efun) ->
{Size1, [Type,{unit,Unit},Sign,Endian]} =
make_bit_type(Line, Size0, Options0),
- match_check_size(Mfun, Size1, BBs0),
{value, Size, _BBs} = Efun(Size1, BBs0),
F = fun(C, Bin, Bs, BBs) ->
bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian,
@@ -200,7 +210,6 @@ bin_gen_field({bin_element,Line,VE,Size0,Options0},
make_bit_type(Line, Size0, Options0),
V = erl_eval:partial_eval(VE),
NewV = coerce_to_float(V, Type),
- match_check_size(Mfun, Size1, BBs0, false),
{value, Size, _BBs} = Efun(Size1, BBs0),
bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun).
@@ -269,7 +278,6 @@ match_field_1({bin_element,Line,{string,SLine,S},Size0,Options0},
{Size1, [Type,{unit,Unit},Sign,Endian]} =
make_bit_type(Line, Size0, Options0),
Size2 = erl_eval:partial_eval(Size1),
- match_check_size(Mfun, Size2, BBs0),
{value, Size, _BBs} = Efun(Size2, BBs0),
F = fun(C, Bin, Bs, BBs) ->
match_field(Bin, Type, Size, Unit, Sign, Endian,
@@ -283,7 +291,6 @@ match_field_1({bin_element,Line,VE,Size0,Options0},
V = erl_eval:partial_eval(VE),
NewV = coerce_to_float(V, Type),
Size2 = erl_eval:partial_eval(Size1),
- match_check_size(Mfun, Size2, BBs0),
{value, Size, _BBs} = Efun(Size2, BBs0),
match_field(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun).
@@ -331,12 +338,18 @@ get_value(Bin, utf16, undefined, _Unit, _Sign, big) ->
get_value(Bin, utf16, undefined, _Unit, _Sign, little) ->
<<I/little-utf16,Rest/bits>> = Bin,
{I,Rest};
+get_value(Bin, utf16, undefined, _Unit, _Sign, native) ->
+ <<I/native-utf16,Rest/bits>> = Bin,
+ {I,Rest};
get_value(Bin, utf32, undefined, _Unit, _Sign, big) ->
<<Val/big-utf32,Rest/bits>> = Bin,
{Val,Rest};
get_value(Bin, utf32, undefined, _Unit, _Sign, little) ->
<<Val/little-utf32,Rest/bits>> = Bin,
{Val,Rest};
+get_value(Bin, utf32, undefined, _Unit, _Sign, native) ->
+ <<Val/native-utf32,Rest/bits>> = Bin,
+ {Val,Rest};
get_value(Bin, binary, all, Unit, _Sign, _Endian) ->
0 = (bit_size(Bin) rem Unit),
{Bin,<<>>};
@@ -387,24 +400,3 @@ make_bit_type(_Line, Size, Type0) -> %Size evaluates to an integer or 'all'
{ok,Size,Bt} -> {Size,erl_bits:as_list(Bt)};
{error,Reason} -> erlang:raise(error, Reason, ?STACKTRACE)
end.
-
-match_check_size(Mfun, Size, Bs) ->
- match_check_size(Mfun, Size, Bs, true).
-
-match_check_size(Mfun, {var,_,V}, Bs, _AllowAll) ->
- case Mfun(binding, {V,Bs}) of
- {value,_} -> ok;
- unbound -> throw(invalid) % or, rather, error({unbound,V})
- end;
-match_check_size(_, {atom,_,all}, _Bs, true) ->
- ok;
-match_check_size(_, {atom,_,all}, _Bs, false) ->
- throw(invalid);
-match_check_size(_, {atom,_,undefined}, _Bs, _AllowAll) ->
- ok;
-match_check_size(_, {integer,_,_}, _Bs, _AllowAll) ->
- ok;
-match_check_size(_, {value,_,_}, _Bs, _AllowAll) ->
- ok; %From the debugger.
-match_check_size(_, _, _Bs, _AllowAll) ->
- throw(invalid).
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index f72d54d13d..7f8c282a79 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -25,6 +25,7 @@
-export([wildcard/3, is_dir/2, is_file/2, is_regular/2]).
-export([fold_files/6, last_modified/2, file_size/2]).
-export([find_file/2, find_file/3, find_source/1, find_source/2, find_source/3]).
+-export([safe_relative_path/2]).
%% For debugging/testing.
-export([compile_wildcard/1]).
@@ -267,9 +268,9 @@ do_wildcard(Pattern, Cwd, Mod) ->
lists:sort(Files).
do_wildcard_1({exists,File}, Mod) ->
- case eval_read_link_info(File, Mod) of
- {ok,_} -> [File];
- _ -> []
+ case exists(File, Mod) of
+ true -> [File];
+ false -> []
end;
do_wildcard_1([Base|Rest], Mod) ->
do_wildcard_2([Base], Rest, [], Mod).
@@ -333,6 +334,7 @@ match_part([_|_], []) ->
false.
will_always_match([accept]) -> true;
+will_always_match([double_star]) -> true;
will_always_match(_) -> false.
prepare_base(Base0) ->
@@ -340,22 +342,33 @@ prepare_base(Base0) ->
"x"++Base2 = lists:reverse(Base1),
lists:reverse(Base2).
-do_double_star(Base, [H|T], Rest, Result, Mod, Root) ->
+do_double_star(Base, [H|T], Patterns, Result0, Mod, Root) ->
Full = case Root of
- false -> filename:join(Base, H);
- true -> H
- end,
+ false -> filename:join(Base, H);
+ true -> H
+ end,
Result1 = case do_list_dir(Full, Mod) of
- {ok, Files} ->
- do_double_star(Full, Files, Rest, Result, Mod, false);
- _ -> Result
- end,
- Result2 = case Root andalso Rest == [] of
- true -> Result1;
- false -> do_wildcard_3(Full, Rest, Result1, Mod)
- end,
- do_double_star(Base, T, Rest, Result2, Mod, Root);
-do_double_star(_Base, [], _Rest, Result, _Mod, _Root) ->
+ {ok, Files} ->
+ do_double_star(Full, Files, Patterns, Result0, Mod, false);
+ _ -> Result0
+ end,
+ Result2 = case Patterns of
+ %% The root is never included in the result.
+ _ when Root -> Result1;
+
+ %% An empty pattern includes all results (except the root).
+ [] -> [Full | Result1];
+
+ %% Otherwise we check if the current entry matches
+ %% and continue recursively.
+ [Pattern | Rest] ->
+ case match_part(Pattern, H) of
+ true -> do_wildcard_2([Full], Rest, Result1, Mod);
+ false -> Result1
+ end
+ end,
+ do_double_star(Base, T, Patterns, Result2, Mod, Root);
+do_double_star(_Base, [], _Patterns, Result, _Mod, _Root) ->
Result.
do_star(Pattern, [_|Rest]=File) ->
@@ -549,6 +562,36 @@ wrap_escapes([]) ->
badpattern(Reason) ->
error({badpattern,Reason}).
+exists(File, Mod) ->
+ case eval_read_link_info(File, Mod) of
+ {error, _} ->
+ false;
+ {ok, _Info} ->
+ case os:type() of
+ {win32,_} ->
+ do_exists(filename:split(File), Mod, []);
+ _ ->
+ true
+ end
+ end.
+
+do_exists([P,".."|Ps], Mod, Acc) ->
+ %% On Windows, "pathname/.." will seem to exist even if pathname
+ %% does not refer to a directory.
+ Path = case Acc of
+ [] -> P;
+ _ -> filename:join(lists:reverse(Acc, [P]))
+ end,
+ case eval_read_link_info(Path, Mod) of
+ {ok, #file_info{type=directory}} ->
+ do_exists(Ps, Mod, Acc);
+ _ ->
+ false
+ end;
+do_exists([P|Ps], Mod, Acc) ->
+ do_exists(Ps, Mod, [P|Acc]);
+do_exists([], _, _) -> true.
+
eval_read_file_info(File, file) ->
file:read_file_info(File);
eval_read_file_info(File, erl_prim_loader) ->
@@ -706,3 +749,71 @@ find_regular_file([File|Files]) ->
true -> {ok, File};
false -> find_regular_file(Files)
end.
+
+-spec safe_relative_path(Filename, Cwd) -> unsafe | SafeFilename when
+ Filename :: filename_all(),
+ Cwd :: filename_all(),
+ SafeFilename :: filename_all().
+
+safe_relative_path(Path, Cwd) ->
+ case filename:pathtype(Path) of
+ relative -> safe_relative_path(filename:split(Path), Cwd, [], "");
+ _ -> unsafe
+ end.
+
+safe_relative_path([], _Cwd, _PrevLinks, Acc) ->
+ Acc;
+
+safe_relative_path([Segment | Segments], Cwd, PrevLinks, Acc) ->
+ AccSegment = join(Acc, Segment),
+ case safe_relative_path(AccSegment) of
+ unsafe ->
+ unsafe;
+ SafeAccSegment ->
+ case file:read_link(join(Cwd, SafeAccSegment)) of
+ {ok, LinkPath} ->
+ case lists:member(LinkPath, PrevLinks) of
+ true ->
+ unsafe;
+ false ->
+ case safe_relative_path(filename:split(LinkPath), Cwd, [LinkPath | PrevLinks], Acc) of
+ unsafe -> unsafe;
+ NewAcc -> safe_relative_path(Segments, Cwd, [], NewAcc)
+ end
+ end;
+ {error, _} ->
+ safe_relative_path(Segments, Cwd, PrevLinks, SafeAccSegment)
+ end
+ end.
+
+join([], Path) -> Path;
+join(Left, Right) -> filename:join(Left, Right).
+
+safe_relative_path(Path) ->
+ case filename:pathtype(Path) of
+ relative ->
+ Cs0 = filename:split(Path),
+ safe_relative_path_1(Cs0, []);
+ _ ->
+ unsafe
+ end.
+
+safe_relative_path_1(["."|T], Acc) ->
+ safe_relative_path_1(T, Acc);
+safe_relative_path_1([<<".">>|T], Acc) ->
+ safe_relative_path_1(T, Acc);
+safe_relative_path_1([".."|T], Acc) ->
+ climb(T, Acc);
+safe_relative_path_1([<<"..">>|T], Acc) ->
+ climb(T, Acc);
+safe_relative_path_1([H|T], Acc) ->
+ safe_relative_path_1(T, [H|Acc]);
+safe_relative_path_1([], []) ->
+ [];
+safe_relative_path_1([], Acc) ->
+ filename:join(lists:reverse(Acc)).
+
+climb(_, []) ->
+ unsafe;
+climb(T, [_|Acc]) ->
+ safe_relative_path_1(T, Acc).
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index b7b7b562ab..b6df99621f 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -19,8 +19,8 @@
%%
-module(filename).
--deprecated({find_src,1,next_major_release}).
--deprecated({find_src,2,next_major_release}).
+-deprecated([{find_src,'_',"use filelib:find_source/1,3 instead"}]).
+-deprecated([{safe_relative_path,1,"use filelib:safe_relative_path/2 instead"}]).
%% Purpose: Provides generic manipulation of filenames.
%%
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index a7f743bd4c..be14665d80 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -28,7 +28,9 @@
%%%-----------------------------------------------------------------
-export([start/5, start/6, debug_options/2, hibernate_after/1,
name/1, unregister_name/1, get_proc_name/1, get_parent/0,
- call/3, call/4, reply/2, stop/1, stop/3]).
+ call/3, call/4, reply/2,
+ send_request/3, wait_response/2, check_response/2,
+ stop/1, stop/3]).
-export([init_it/6, init_it/7]).
@@ -38,7 +40,7 @@
%%-----------------------------------------------------------------
--type linkage() :: 'link' | 'nolink'.
+-type linkage() :: 'monitor' | 'link' | 'nolink'.
-type emgr_name() :: {'local', atom()}
| {'global', term()}
| {'via', Module :: module(), Name :: term()}.
@@ -53,6 +55,11 @@
| {'spawn_opt', [proc_lib:spawn_option()]}.
-type options() :: [option()].
+-type server_ref() :: pid() | atom() | {atom(), node()}
+ | {global, term()} | {via, module(), term()}.
+
+-type request_id() :: term().
+
%%-----------------------------------------------------------------
%% Starts a generic process.
%% start(GenMod, LinkP, Mod, Args, Options)
@@ -95,6 +102,13 @@ do_spawn(GenMod, link, Mod, Args, Options) ->
[GenMod, self(), self(), Mod, Args, Options],
Time,
spawn_opts(Options));
+do_spawn(GenMod, monitor, Mod, Args, Options) ->
+ Time = timeout(Options),
+ Ret = proc_lib:start_monitor(?MODULE, init_it,
+ [GenMod, self(), self(), Mod, Args, Options],
+ Time,
+ spawn_opts(Options)),
+ monitor_return(Ret);
do_spawn(GenMod, _, Mod, Args, Options) ->
Time = timeout(Options),
proc_lib:start(?MODULE, init_it,
@@ -108,6 +122,13 @@ do_spawn(GenMod, link, Name, Mod, Args, Options) ->
[GenMod, self(), self(), Name, Mod, Args, Options],
Time,
spawn_opts(Options));
+do_spawn(GenMod, monitor, Name, Mod, Args, Options) ->
+ Time = timeout(Options),
+ Ret = proc_lib:start_monitor(?MODULE, init_it,
+ [GenMod, self(), self(), Name, Mod, Args, Options],
+ Time,
+ spawn_opts(Options)),
+ monitor_return(Ret);
do_spawn(GenMod, _, Name, Mod, Args, Options) ->
Time = timeout(Options),
proc_lib:start(?MODULE, init_it,
@@ -115,6 +136,26 @@ do_spawn(GenMod, _, Name, Mod, Args, Options) ->
Time,
spawn_opts(Options)).
+
+%%
+%% Adjust monitor returns for OTP gen behaviours...
+%%
+%% If an OTP behaviour is introduced that 'init_ack's
+%% other results, this has code has to be moved out
+%% into all behaviours as well as adjusted...
+%%
+monitor_return({{ok, Pid}, Mon}) when is_pid(Pid), is_reference(Mon) ->
+ %% Successful start_monitor()...
+ {ok, {Pid, Mon}};
+monitor_return({Error, Mon}) when is_reference(Mon) ->
+ %% Failure; wait for spawned process to terminate
+ %% and release resources, then return the error...
+ receive
+ {'DOWN', Mon, process, _Pid, _Reason} ->
+ ok
+ end,
+ Error.
+
%%-----------------------------------------------------------------
%% Initiate the new process.
%% Register the name using the Rfunc function
@@ -139,7 +180,7 @@ init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
%%-----------------------------------------------------------------
%% Makes a synchronous call to a generic process.
%% Request is sent to the Pid, and the response must be
-%% {Tag, _, Reply}.
+%% {Tag, Reply}.
%%-----------------------------------------------------------------
%%% New call function which uses the new monitor BIF
@@ -192,6 +233,56 @@ get_node(Process) ->
node(Process)
end.
+-spec send_request(Name::server_ref(), Label::term(), Request::term()) -> request_id().
+send_request(Process, Label, Request) when is_pid(Process) ->
+ do_send_request(Process, Label, Request);
+send_request(Process, Label, Request) ->
+ Fun = fun(Pid) -> do_send_request(Pid, Label, Request) end,
+ try do_for_proc(Process, Fun)
+ catch exit:Reason ->
+ %% Make send_request async and fake a down message
+ Mref = erlang:make_ref(),
+ self() ! {'DOWN', Mref, process, Process, Reason},
+ Mref
+ end.
+
+do_send_request(Process, Label, Request) ->
+ Mref = erlang:monitor(process, Process),
+ erlang:send(Process, {Label, {self(), {'$gen_request_id', Mref}}, Request}, [noconnect]),
+ Mref.
+
+%%
+%% Wait for a reply to the client.
+%% Note: if timeout is returned monitors are kept.
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+ {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
+wait_response(Mref, Timeout)
+ when is_reference(Mref) ->
+ receive
+ {{'$gen_request_id', Mref}, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {reply, Reply};
+ {'DOWN', Mref, _, Object, Reason} ->
+ {error, {Reason, Object}}
+ after Timeout ->
+ timeout
+ end.
+
+-spec check_response(RequestId::term(), Key::request_id()) ->
+ {reply, Reply::term()} | 'no_reply' | {error, {term(), server_ref()}}.
+check_response(Msg, Mref)
+ when is_reference(Mref) ->
+ case Msg of
+ {{'$gen_request_id', Mref}, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {reply, Reply};
+ {'DOWN', Mref, _, Object, Reason} ->
+ {error, {Reason, Object}};
+ _ ->
+ no_reply
+ end.
+
%%
%% Send a reply to the client.
%%
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 8213282867..8024221cab 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -31,13 +31,20 @@
%%% Re-written by Joe with new functional interface !
%%% Modified by Martin - uses proc_lib, sys and gen!
+%%%
+%%% NOTE: If init_ack() return values are modified, see comment
+%%% above monitor_return() in gen.erl!
+%%%
-export([start/0, start/1, start/2,
start_link/0, start_link/1, start_link/2,
+ start_monitor/0, start_monitor/1, start_monitor/2,
stop/1, stop/3,
notify/2, sync_notify/2,
add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3,
- swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/5]).
+ swap_sup_handler/3, which_handlers/1, call/3, call/4,
+ send_request/3, wait_response/2, check_response/2,
+ wake_hib/5]).
-export([init_it/6,
system_continue/3,
@@ -48,7 +55,7 @@
format_status/2]).
%% logger callback
--export([format_log/1]).
+-export([format_log/1, format_log/2]).
-export_type([handler/0, handler_args/0, add_handler_ret/0,
del_handler_ret/0]).
@@ -128,11 +135,13 @@
| {'logfile', string()}.
-type option() :: {'timeout', timeout()}
| {'debug', [debug_flag()]}
- | {'spawn_opt', [proc_lib:spawn_option()]}
+ | {'spawn_opt', [proc_lib:start_spawn_option()]}
| {'hibernate_after', timeout()}.
-type emgr_ref() :: atom() | {atom(), atom()} | {'global', term()}
| {'via', atom(), term()} | pid().
-type start_ret() :: {'ok', pid()} | {'error', term()}.
+-type start_mon_ret() :: {'ok', {pid(),reference()}} | {'error', term()}.
+-type request_id() :: term().
%%---------------------------------------------------------------------------
@@ -183,6 +192,20 @@ start_link(Options) when is_list(Options) ->
start_link(Name, Options) ->
gen:start(?MODULE, link, Name, ?NO_CALLBACK, [], Options).
+-spec start_monitor() -> start_mon_ret().
+start_monitor() ->
+ gen:start(?MODULE, monitor, ?NO_CALLBACK, [], []).
+
+-spec start_monitor(emgr_name() | [option()]) -> start_mon_ret().
+start_monitor(Name) when is_tuple(Name) ->
+ gen:start(?MODULE, monitor, Name, ?NO_CALLBACK, [], []);
+start_monitor(Options) when is_list(Options) ->
+ gen:start(?MODULE, monitor, ?NO_CALLBACK, [], Options).
+
+-spec start_monitor(emgr_name(), [option()]) -> start_mon_ret().
+start_monitor(Name, Options) ->
+ gen:start(?MODULE, monitor, Name, ?NO_CALLBACK, [], Options).
+
%% -spec init_it(pid(), 'self' | pid(), emgr_name(), module(), [term()], [_]) ->
init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
@@ -213,6 +236,26 @@ call(M, Handler, Query) -> call1(M, Handler, Query).
-spec call(emgr_ref(), handler(), term(), timeout()) -> term().
call(M, Handler, Query, Timeout) -> call1(M, Handler, Query, Timeout).
+-spec send_request(emgr_ref(), handler(), term()) -> request_id().
+send_request(M, Handler, Query) ->
+ gen:send_request(M, self(), {call, Handler, Query}).
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+ {reply, Reply::term()} | 'timeout' | {error, {Reason::term(), emgr_ref()}}.
+wait_response(RequestId, Timeout) ->
+ case gen:wait_response(RequestId, Timeout) of
+ {reply, {error, _} = Err} -> Err;
+ Return -> Return
+ end.
+
+-spec check_response(Msg::term(), RequestId::request_id()) ->
+ {reply, Reply::term()} | 'no_reply' | {error, {Reason::term(), emgr_ref()}}.
+check_response(Msg, RequestId) ->
+ case gen:check_response(Msg, RequestId) of
+ {reply, {error, _} = Err} -> Err;
+ Return -> Return
+ end.
+
-spec delete_handler(emgr_ref(), handler(), term()) -> term().
delete_handler(M, Handler, Args) -> rpc(M, {delete_handler, Handler, Args}).
@@ -590,8 +633,10 @@ server_update(Handler1, Func, Event, SName) ->
module=>Mod1,
message=>Event},
#{domain=>[otp],
- report_cb=>fun gen_event:format_log/1,
- error_logger=>#{tag=>warning_msg}}), % warningmap??
+ report_cb=>fun gen_event:format_log/2,
+ error_logger=>
+ #{tag=>warning_msg, % warningmap??
+ report_cb=>fun gen_event:format_log/1}}),
{ok, Handler1};
Other ->
do_terminate(Mod1, Handler1, {error, Other}, State,
@@ -752,46 +797,165 @@ report_error(Handler, Reason, State, LastIn, SName) ->
get(),State),
reason=>Reason},
#{domain=>[otp],
- report_cb=>fun gen_event:format_log/1,
- error_logger=>#{tag=>error}}).
-
-format_log(#{label:={gen_event,terminate},
- handler:=Handler,
- name:=SName,
- last_message:=LastIn,
- state:=State,
- reason:=Reason}) ->
- Reason1 =
- case Reason of
- {'EXIT',{undef,[{M,F,A,L}|MFAs]}} ->
- case code:is_loaded(M) of
- false ->
- {'module could not be loaded',[{M,F,A,L}|MFAs]};
- _ ->
- case erlang:function_exported(M, F, length(A)) of
- true ->
- {undef,[{M,F,A,L}|MFAs]};
- false ->
- {'function not exported',[{M,F,A,L}|MFAs]}
- end
- end;
- {'EXIT',Why} ->
- Why;
- _ ->
- Reason
- end,
- {"** gen_event handler ~p crashed.~n"
- "** Was installed in ~tp~n"
- "** Last event was: ~tp~n"
- "** When handler state == ~tp~n"
- "** Reason == ~tp~n",
- [Handler,SName,LastIn,State,Reason1]};
-format_log(#{label:={gen_event,no_handle_info},
- module:=Mod,
- message:=Msg}) ->
- {"** Undefined handle_info in ~tp~n"
- "** Unhandled message: ~tp~n",
- [Mod, Msg]}.
+ report_cb=>fun gen_event:format_log/2,
+ error_logger=>#{tag=>error,
+ report_cb=>fun gen_event:format_log/1}}).
+
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(Report) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(Report, Depth), FormatOpts).
+
+limit_report(Report, unlimited) ->
+ Report;
+limit_report(#{label:={gen_event,terminate},
+ last_message:=LastIn,
+ state:=State,
+ reason:=Reason}=Report,
+ Depth) ->
+ Report#{last_message => io_lib:limit_term(LastIn, Depth),
+ state => io_lib:limit_term(State, Depth),
+ reason => io_lib:limit_term(Reason, Depth)};
+limit_report(#{label:={gen_event,no_handle_info},
+ message:=Msg}=Report,
+ Depth) ->
+ Report#{message => io_lib:limit_term(Msg, Depth)}.
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default, FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={gen_event,terminate},
+ handler:=Handler,
+ name:=SName,
+ last_message:=LastIn,
+ state:=State,
+ reason:=Reason},
+ #{single_line:=true, depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Reason1 = fix_reason(Reason),
+ Format1 = lists:append(["Generic event handler ",P," crashed. "
+ "Installed: ",P,". Last event: ",P,
+ ". State: ",P,". Reason: ",P,"."]),
+ Args1 =
+ case Depth of
+ unlimited ->
+ [Handler,SName,Reason1,LastIn,State];
+ _ ->
+ [Handler,Depth,SName,Depth,Reason1,Depth,
+ LastIn,Depth,State,Depth]
+ end,
+ {Format1, Args1};
+format_log_single(#{label:={gen_event,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Undefined handle_info in ",P,
+ ". Unhandled message: ",P,"."]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Depth,Msg,Depth]
+ end,
+ {Format,Args};
+format_log_single(Report,FormatOpts) ->
+ format_log_multi(Report,FormatOpts).
+
+format_log_multi(#{label:={gen_event,terminate},
+ handler:=Handler,
+ name:=SName,
+ last_message:=LastIn,
+ state:=State,
+ reason:=Reason},
+ #{depth:=Depth}=FormatOpts) ->
+ Reason1 = fix_reason(Reason),
+ P = p(FormatOpts),
+ Format =
+ lists:append(["** gen_event handler ",P," crashed.\n",
+ "** Was installed in ",P,"\n",
+ "** Last event was: ",P,"\n",
+ "** When handler state == ",P,"\n",
+ "** Reason == ",P,"\n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Handler,SName,LastIn,State,Reason1];
+ _ ->
+ [Handler,Depth,SName,Depth,LastIn,Depth,State,Depth,
+ Reason1,Depth]
+ end,
+ {Format,Args};
+format_log_multi(#{label:={gen_event,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ "** Undefined handle_info in ~p\n"
+ "** Unhandled message: "++P++"\n",
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Msg,Depth]
+ end,
+ {Format,Args}.
+
+fix_reason({'EXIT',{undef,[{M,F,A,_L}|_]=MFAs}=Reason}) ->
+ case code:is_loaded(M) of
+ false ->
+ {'module could not be loaded',MFAs};
+ _ ->
+ case erlang:function_exported(M, F, length(A)) of
+ true ->
+ Reason;
+ false ->
+ {'function not exported',MFAs}
+ end
+ end;
+fix_reason({'EXIT',Reason}) ->
+ Reason;
+fix_reason(Reason) ->
+ Reason.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
handler(Handler) when not Handler#handler.id ->
Handler#handler.module;
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 1e18710738..f4752c37d4 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -127,27 +127,9 @@
format_status/2]).
%% logger callback
--export([format_log/1]).
-
--deprecated({start, 3, eventually}).
--deprecated({start, 4, eventually}).
--deprecated({start_link, 3, eventually}).
--deprecated({start_link, 4, eventually}).
--deprecated({stop, 1, eventually}).
--deprecated({stop, 3, eventually}).
--deprecated({send_event, 2, eventually}).
--deprecated({sync_send_event, 2, eventually}).
--deprecated({sync_send_event, 3, eventually}).
--deprecated({send_all_state_event, 2, eventually}).
--deprecated({sync_send_all_state_event, 2, eventually}).
--deprecated({sync_send_all_state_event, 3, eventually}).
--deprecated({reply, 2, eventually}).
--deprecated({start_timer, 2, eventually}).
--deprecated({send_event_after, 2, eventually}).
--deprecated({cancel_timer, 1, eventually}).
--deprecated({enter_loop, 4, eventually}).
--deprecated({enter_loop, 5, eventually}).
--deprecated({enter_loop, 6, eventually}).
+-export([format_log/1, format_log/2]).
+
+-deprecated({'_','_', "use the 'gen_statem' module instead"}).
%%% ---------------------------------------------------
%%% Interface functions.
@@ -517,8 +499,10 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTi
module=>Mod,
message=>Msg},
#{domain=>[otp],
- report_cb=>fun gen_fsm:format_log/1,
- error_logger=>#{tag=>warning_msg}}),
+ report_cb=>fun gen_fsm:format_log/2,
+ error_logger=>
+ #{tag=>warning_msg,
+ report_cb=>fun gen_fsm:format_log/1}}),
loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, []);
{'EXIT', What} ->
terminate(What, Name, From, Msg, Mod, StateName, StateData, []);
@@ -634,8 +618,9 @@ error_info(Reason, Name, From, Msg, StateName, StateData, Debug) ->
reason=>Reason,
client_info=>client_stacktrace(From)},
#{domain=>[otp],
- report_cb=>fun gen_fsm:format_log/1,
- error_logger=>#{tag=>error}}),
+ report_cb=>fun gen_fsm:format_log/2,
+ error_logger=>#{tag=>error,
+ report_cb=>fun gen_fsm:format_log/1}}),
ok.
client_stacktrace(undefined) ->
@@ -655,70 +640,197 @@ client_stacktrace(Pid) when is_pid(Pid) ->
{Pid,remote}.
-format_log(#{label:={gen_fsm,terminate},
- name:=Name,
- last_message:=Msg,
- state_name:=StateName,
- state_data:=StateData,
- log:=Log,
- reason:=Reason,
- client_info:=ClientInfo}) ->
- Reason1 =
- case Reason of
- {undef,[{M,F,A,L}|MFAs]} ->
- case code:is_loaded(M) of
- false ->
- {'module could not be loaded',[{M,F,A,L}|MFAs]};
- _ ->
- case erlang:function_exported(M, F, length(A)) of
- true ->
- Reason;
- false ->
- {'function not exported',[{M,F,A,L}|MFAs]}
- end
- end;
- _ ->
- Reason
- end,
- {ClientFmt,ClientArgs} = format_client_log(ClientInfo),
- {"** State machine ~tp terminating \n" ++
- get_msg_str(Msg) ++
- "** When State == ~tp~n"
- "** Data == ~tp~n"
- "** Reason for termination ==~n** ~tp~n" ++
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(Report) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(Report, Depth), FormatOpts).
+
+limit_report(Report, unlimited) ->
+ Report;
+limit_report(#{label:={gen_fsm,terminate},
+ last_message:=Msg,
+ state_data:=StateData,
+ log:=Log,
+ reason:=Reason,
+ client_info:=ClientInfo}=Report,
+ Depth) ->
+ Report#{last_message=>io_lib:limit_term(Msg, Depth),
+ state_data=>io_lib:limit_term(StateData, Depth),
+ log=>[io_lib:limit_term(L, Depth) || L <- Log],
+ reason=>io_lib:limit_term(Reason, Depth),
+ client_info=>limit_client_report(ClientInfo, Depth)};
+limit_report(#{label:={gen_fsm,no_handle_info},
+ message:=Msg}=Report, Depth) ->
+ Report#{message=>io_lib:limit_term(Msg, Depth)}.
+
+limit_client_report({From,{Name,Stacktrace}}, Depth) ->
+ {From,{Name,io_lib:limit_term(Stacktrace, Depth)}};
+limit_client_report(Client, _) ->
+ Client.
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default, FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={gen_fsm,terminate},
+ name:=Name,
+ last_message:=Msg,
+ state_name:=StateName,
+ state_data:=StateData,
+ log:=Log,
+ reason:=Reason,
+ client_info:=ClientInfo},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ FixedReason = fix_reason(Reason),
+ {ClientFmt,ClientArgs} = format_client_log_single(ClientInfo, P, Depth),
+ Format =
+ lists:append(
+ ["State machine ",P," terminating. Reason: ",P,
+ ". Last event: ",P,
+ ". State: ",P,
+ ". Data: ",P,
+ case Log of
+ [] -> "";
+ _ -> ". Log: "++P
+ end,
+ "."]),
+ Args0 =
+ [Name,FixedReason,get_msg(Msg),StateName,StateData] ++
+ case Log of
+ [] -> [];
+ _ -> [Log]
+ end,
+ Args = case Depth of
+ unlimited ->
+ Args0;
+ _ ->
+ lists:flatmap(fun(A) -> [A, Depth] end, Args0)
+ end,
+ {Format++ClientFmt, Args++ClientArgs};
+format_log_single(#{label:={gen_fsm,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Undefined handle_info in ",P,
+ ". Unhandled message: ",P,"."]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Depth,Msg,Depth]
+ end,
+ {Format,Args};
+format_log_single(Report, FormatOpts) ->
+ format_log_multi(Report, FormatOpts).
+
+format_log_multi(#{label:={gen_fsm,terminate},
+ name:=Name,
+ last_message:=Msg,
+ state_name:=StateName,
+ state_data:=StateData,
+ log:=Log,
+ reason:=Reason,
+ client_info:=ClientInfo},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ FixedReason = fix_reason(Reason),
+ {ClientFmt,ClientArgs} = format_client_log(ClientInfo, P, Depth),
+ Format =
+ lists:append(
+ ["** State machine ",P," terminating \n"++
+ get_msg_str(Msg, P)++
+ "** When State == ",P,"~n",
+ "** Data == ",P,"~n",
+ "** Reason for termination ==~n** ",P,"~n",
+ case Log of
+ [] -> [];
+ _ -> "** Log ==~n**"++P++"~n"
+ end]),
+ Args0 =
+ [Name|get_msg(Msg)] ++
+ [StateName,StateData,FixedReason |
case Log of
[] -> [];
- _ -> "** Log ==~n** ~tp~n"
- end ++ ClientFmt,
- [Name|error_logger:limit_term(get_msg(Msg))] ++
- [StateName,
- error_logger:limit_term(StateData),
- error_logger:limit_term(Reason1) |
- case Log of
- [] -> [];
- _ -> [[error_logger:limit_term(D) || D <- Log]]
- end] ++ ClientArgs};
-format_log(#{label:={gen_fsm,no_handle_info},
- module:=Mod,
- message:=Msg}) ->
- {"** Undefined handle_info in ~p~n"
- "** Unhandled message: ~tp~n",
- [Mod, error_logger:limit_term(Msg)]}.
-
-get_msg_str({'$gen_event', _Event}) ->
- "** Last event in was ~tp~n";
-get_msg_str({'$gen_sync_event', _From, _Event}) ->
- "** Last sync event in was ~tp from ~tw~n";
-get_msg_str({'$gen_all_state_event', _Event}) ->
- "** Last event in was ~tp (for all states)~n";
-get_msg_str({'$gen_sync_all_state_event', _From, _Event}) ->
- "** Last sync event in was ~tp (for all states) from ~tw~n";
-get_msg_str({timeout, _Ref, {'$gen_timer', _Msg}}) ->
- "** Last timer event in was ~tp~n";
-get_msg_str({timeout, _Ref, {'$gen_event', _Msg}}) ->
- "** Last timer event in was ~tp~n";
-get_msg_str(_Msg) ->
- "** Last message in was ~tp~n".
+ _ -> [Log]
+ end],
+ Args = case Depth of
+ unlimited ->
+ Args0;
+ _ ->
+ lists:flatmap(fun(A) -> [A, Depth] end, Args0)
+ end,
+ {Format++ClientFmt,Args++ClientArgs};
+format_log_multi(#{label:={gen_fsm,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ "** Undefined handle_info in ~p~n"
+ "** Unhandled message: "++P++"~n",
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Msg,Depth]
+ end,
+ {Format,Args}.
+
+fix_reason({undef,[{M,F,A,L}|MFAs]}=Reason) ->
+ case code:is_loaded(M) of
+ false ->
+ {'module could not be loaded',[{M,F,A,L}|MFAs]};
+ _ ->
+ case erlang:function_exported(M, F, length(A)) of
+ true ->
+ Reason;
+ false ->
+ {'function not exported',[{M,F,A,L}|MFAs]}
+ end
+ end;
+fix_reason(Reason) ->
+ Reason.
+
+get_msg_str({'$gen_event', _Event}, P) ->
+ "** Last event in was "++P++"~n";
+get_msg_str({'$gen_sync_event', _From, _Event}, P) ->
+ "** Last sync event in was "++P++" from ~tw~n";
+get_msg_str({'$gen_all_state_event', _Event}, P) ->
+ "** Last event in was "++P++" (for all states)~n";
+get_msg_str({'$gen_sync_all_state_event', _From, _Event}, P) ->
+ "** Last sync event in was "++P++" (for all states) from "++P++"~n";
+get_msg_str({timeout, _Ref, {'$gen_timer', _Msg}}, P) ->
+ "** Last timer event in was "++P++"~n";
+get_msg_str({timeout, _Ref, {'$gen_event', _Msg}}, P) ->
+ "** Last timer event in was "++P++"~n";
+get_msg_str(_Msg, P) ->
+ "** Last message in was "++P++"~n".
get_msg({'$gen_event', Event}) -> [Event];
get_msg({'$gen_sync_event', {From,_Tag}, Event}) -> [Event,From];
@@ -728,16 +840,53 @@ get_msg({timeout, Ref, {'$gen_timer', Msg}}) -> [{timeout, Ref, Msg}];
get_msg({timeout, _Ref, {'$gen_event', Event}}) -> [Event];
get_msg(Msg) -> [Msg].
-format_client_log(undefined) ->
+format_client_log_single(undefined, _, _) ->
+ {"", []};
+format_client_log_single({Pid,dead}, _, _) ->
+ {" Client ~0p is dead.", [Pid]};
+format_client_log_single({Pid,remote}, _, _) ->
+ {" Client ~0p is remote on node ~0p.", [Pid,node(Pid)]};
+format_client_log_single({_Pid,{Name,Stacktrace0}}, P, Depth) ->
+ %% Minimize the stacktrace a bit for single line reports. This is
+ %% hopefully enough to point out the position.
+ Stacktrace = lists:sublist(Stacktrace0, 4),
+ Format = lists:append([" Client ",P," stacktrace: ",P,"."]),
+ Args = case Depth of
+ unlimited ->
+ [Name, Stacktrace];
+ _ ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format, Args}.
+
+format_client_log(undefined, _, _) ->
{"", []};
-format_client_log({From,dead}) ->
- {"** Client ~p is dead~n", [From]};
-format_client_log({From,remote}) ->
- {"** Client ~p is remote on node ~p~n", [From, node(From)]};
-format_client_log({_From,{Name,Stacktrace}}) ->
- {"** Client ~tp stacktrace~n"
- "** ~tp~n",
- [Name, error_logger:limit_term(Stacktrace)]}.
+format_client_log({Pid,dead}, _, _) ->
+ {"** Client ~p is dead~n", [Pid]};
+format_client_log({Pid,remote}, _, _) ->
+ {"** Client ~p is remote on node ~p~n", [Pid,node(Pid)]};
+format_client_log({_Pid,{Name,Stacktrace}}, P, Depth) ->
+ Format = lists:append(["** Client ",P," stacktrace~n** ",P,"~n"]),
+ Args = case Depth of
+ unlimited ->
+ [Name, Stacktrace];
+ _ ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format,Args}.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
%%-----------------------------------------------------------------
%% Status information
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index c7b6406f54..3638027d4f 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -19,6 +19,11 @@
%%
-module(gen_server).
+%%%
+%%% NOTE: If init_ack() return values are modified, see comment
+%%% above monitor_return() in gen.erl!
+%%%
+
%%% ---------------------------------------------------
%%%
%%% The idea behind THIS server is that the user module
@@ -89,8 +94,10 @@
%% API
-export([start/3, start/4,
start_link/3, start_link/4,
+ start_monitor/3, start_monitor/4,
stop/1, stop/3,
call/2, call/3,
+ send_request/2, wait_response/2, check_response/2,
cast/2, reply/2,
abcast/2, abcast/3,
multi_call/2, multi_call/3, multi_call/4,
@@ -105,7 +112,7 @@
format_status/2]).
%% logger callback
--export([format_log/1]).
+-export([format_log/1, format_log/2]).
%% Internal exports
-export([init_it/6]).
@@ -116,6 +123,16 @@
STACKTRACE(),
element(2, erlang:process_info(self(), current_stacktrace))).
+
+-type server_ref() ::
+ pid()
+ | (LocalName :: atom())
+ | {Name :: atom(), Node :: atom()}
+ | {'global', GlobalName :: term()}
+ | {'via', RegMod :: module(), ViaName :: term()}.
+
+-type request_id() :: term().
+
%%%=========================================================================
%%% API
%%%=========================================================================
@@ -188,6 +205,12 @@ start_link(Mod, Args, Options) ->
start_link(Name, Mod, Args, Options) ->
gen:start(?MODULE, link, Name, Mod, Args, Options).
+start_monitor(Mod, Args, Options) ->
+ gen:start(?MODULE, monitor, Mod, Args, Options).
+
+start_monitor(Name, Mod, Args, Options) ->
+ gen:start(?MODULE, monitor, Name, Mod, Args, Options).
+
%% -----------------------------------------------------------------
%% Stop a generic server and wait for it to terminate.
@@ -224,6 +247,25 @@ call(Name, Request, Timeout) ->
end.
%% -----------------------------------------------------------------
+%% Send a request to a generic server and return a Key which should be
+%% used with wait_response/2 or check_response/2 to fetch the
+%% result of the request.
+
+-spec send_request(Name::server_ref(), Request::term()) -> request_id().
+send_request(Name, Request) ->
+ gen:send_request(Name, '$gen_call', Request).
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+ {reply, Reply::term()} | 'timeout' | {error, {Reason::term(), server_ref()}}.
+wait_response(RequestId, Timeout) ->
+ gen:wait_response(RequestId, Timeout).
+
+-spec check_response(Msg::term(), RequestId::request_id()) ->
+ {reply, Reply::term()} | 'no_reply' | {error, {Reason::term(), server_ref()}}.
+check_response(Msg, RequestId) ->
+ gen:check_response(Msg, RequestId).
+
+%% -----------------------------------------------------------------
%% Make a cast to a generic server.
%% -----------------------------------------------------------------
cast({global,Name}, Request) ->
@@ -249,7 +291,8 @@ cast_msg(Request) -> {'$gen_cast',Request}.
%% Send a reply to the client.
%% -----------------------------------------------------------------
reply({To, Tag}, Reply) ->
- catch To ! {Tag, Reply}.
+ catch To ! {Tag, Reply},
+ ok.
%% -----------------------------------------------------------------
%% Asynchronous broadcast, returns nothing, it's just send 'n' pray
@@ -448,6 +491,15 @@ do_send(Dest, Msg) ->
end,
ok.
+do_multi_call([Node], Name, Req, infinity) when Node =:= node() ->
+ % Special case when multi_call is used with local node only.
+ % In that case we can leverage the benefit of recv_mark optimisation
+ % existing in simple gen:call.
+ try gen:call(Name, '$gen_call', Req, infinity) of
+ {ok, Res} -> {[{Node, Res}],[]}
+ catch exit:_ ->
+ {[], [Node]}
+ end;
do_multi_call(Nodes, Name, Req, infinity) ->
Tag = make_ref(),
Monitors = send_nodes(Nodes, Name, Tag, Req),
@@ -646,8 +698,10 @@ try_dispatch(Mod, Func, Msg, State) ->
module=>Mod,
message=>Msg},
#{domain=>[otp],
- report_cb=>fun gen_server:format_log/1,
- error_logger=>#{tag=>warning_msg}}),
+ report_cb=>fun gen_server:format_log/2,
+ error_logger=>
+ #{tag=>warning_msg,
+ report_cb=>fun gen_server:format_log/1}}),
{ok, {noreply, State}};
true ->
{'EXIT', error, R, Stacktrace}
@@ -894,8 +948,9 @@ error_info(Reason, Name, From, Msg, Mod, State, Debug) ->
reason=>Reason,
client_info=>client_stacktrace(From)},
#{domain=>[otp],
- report_cb=>fun gen_server:format_log/1,
- error_logger=>#{tag=>error}}),
+ report_cb=>fun gen_server:format_log/2,
+ error_logger=>#{tag=>error,
+ report_cb=>fun gen_server:format_log/1}}),
ok.
client_stacktrace(undefined) ->
@@ -914,63 +969,236 @@ client_stacktrace(From) when is_pid(From), node(From) =:= node() ->
client_stacktrace(From) when is_pid(From) ->
{From,remote}.
-format_log(#{label:={gen_server,terminate},
- name:=Name,
- last_message:=Msg,
- state:=State,
- log:=Log,
- reason:=Reason,
- client_info:=Client}) ->
- Reason1 =
- case Reason of
- {undef,[{M,F,A,L}|MFAs]} ->
- case code:is_loaded(M) of
- false ->
- {'module could not be loaded',[{M,F,A,L}|MFAs]};
- _ ->
- case erlang:function_exported(M, F, length(A)) of
- true ->
- Reason;
- false ->
- {'function not exported',[{M,F,A,L}|MFAs]}
- end
- end;
- _ ->
- Reason
- end,
- {ClientFmt,ClientArgs} = format_client_log(Client),
- [LimitedMsg,LimitedState,LimitedReason|LimitedLog] =
- [error_logger:limit_term(D) || D <- [Msg,State,Reason1|Log]],
- {"** Generic server ~tp terminating \n"
- "** Last message in was ~tp~n"
- "** When Server state == ~tp~n"
- "** Reason for termination ==~n** ~tp~n" ++
- case LimitedLog of
- [] -> [];
- _ -> "** Log ==~n** ~tp~n"
- end ++ ClientFmt,
- [Name, LimitedMsg, LimitedState, LimitedReason] ++
- case LimitedLog of
- [] -> [];
- _ -> [LimitedLog]
- end ++ ClientArgs};
-format_log(#{label:={gen_server,no_handle_info},
- module:=Mod,
- message:=Msg}) ->
- {"** Undefined handle_info in ~p~n"
- "** Unhandled message: ~tp~n",
- [Mod, error_logger:limit_term(Msg)]}.
-
-format_client_log(undefined) ->
+
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(Report) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(Report,Depth),FormatOpts).
+
+limit_report(Report,unlimited) ->
+ Report;
+limit_report(#{label:={gen_server,terminate},
+ last_message:=Msg,
+ state:=State,
+ log:=Log,
+ reason:=Reason,
+ client_info:=Client}=Report,
+ Depth) ->
+ Report#{last_message=>io_lib:limit_term(Msg,Depth),
+ state=>io_lib:limit_term(State,Depth),
+ log=>[io_lib:limit_term(L,Depth)||L<-Log],
+ reason=>io_lib:limit_term(Reason,Depth),
+ client_info=>limit_client_report(Client,Depth)};
+limit_report(#{label:={gen_server,no_handle_info},
+ message:=Msg}=Report,Depth) ->
+ Report#{message=>io_lib:limit_term(Msg,Depth)}.
+
+limit_client_report({From,{Name,Stacktrace}},Depth) ->
+ {From,{Name,io_lib:limit_term(Stacktrace,Depth)}};
+limit_client_report(Client,_) ->
+ Client.
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default,FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={gen_server,terminate},
+ name:=Name,
+ last_message:=Msg,
+ state:=State,
+ log:=Log,
+ reason:=Reason,
+ client_info:=Client},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format1 = lists:append(["Generic server ",P," terminating. Reason: ",P,
+ ". Last message: ", P, ". State: ",P,"."]),
+ {ServerLogFormat,ServerLogArgs} = format_server_log_single(Log,FormatOpts),
+ {ClientLogFormat,ClientLogArgs} = format_client_log_single(Client,FormatOpts),
+
+ Args1 =
+ case Depth of
+ unlimited ->
+ [Name,fix_reason(Reason),Msg,State];
+ _ ->
+ [Name,Depth,fix_reason(Reason),Depth,Msg,Depth,State,Depth]
+ end,
+ {Format1++ServerLogFormat++ClientLogFormat,
+ Args1++ServerLogArgs++ClientLogArgs};
+format_log_single(#{label:={gen_server,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Undefined handle_info in ",P,
+ ". Unhandled message: ",P,"."]),
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Depth,Msg,Depth]
+ end,
+ {Format,Args};
+format_log_single(Report,FormatOpts) ->
+ format_log_multi(Report,FormatOpts).
+
+format_log_multi(#{label:={gen_server,terminate},
+ name:=Name,
+ last_message:=Msg,
+ state:=State,
+ log:=Log,
+ reason:=Reason,
+ client_info:=Client},
+ #{depth:=Depth}=FormatOpts) ->
+ Reason1 = fix_reason(Reason),
+ {ClientFmt,ClientArgs} = format_client_log(Client,FormatOpts),
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ ["** Generic server ",P," terminating \n"
+ "** Last message in was ",P,"~n"
+ "** When Server state == ",P,"~n"
+ "** Reason for termination ==~n** ",P,"~n"] ++
+ case Log of
+ [] -> [];
+ _ -> ["** Log ==~n** ["|
+ lists:join(",~n ",lists:duplicate(length(Log),P))]++
+ ["]~n"]
+ end) ++ ClientFmt,
+ Args =
+ case Depth of
+ unlimited ->
+ [Name, Msg, State, Reason1] ++
+ case Log of
+ [] -> [];
+ _ -> Log
+ end ++ ClientArgs;
+ _ ->
+ [Name, Depth, Msg, Depth, State, Depth, Reason1, Depth] ++
+ case Log of
+ [] -> [];
+ _ -> lists:flatmap(fun(L) -> [L, Depth] end, Log)
+ end ++ ClientArgs
+ end,
+ {Format,Args};
+format_log_multi(#{label:={gen_server,no_handle_info},
+ module:=Mod,
+ message:=Msg},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ "** Undefined handle_info in ~p~n"
+ "** Unhandled message: "++P++"~n",
+ Args =
+ case Depth of
+ unlimited ->
+ [Mod,Msg];
+ _ ->
+ [Mod,Msg,Depth]
+ end,
+ {Format,Args}.
+
+fix_reason({undef,[{M,F,A,L}|MFAs]}=Reason) ->
+ case code:is_loaded(M) of
+ false ->
+ {'module could not be loaded',[{M,F,A,L}|MFAs]};
+ _ ->
+ case erlang:function_exported(M, F, length(A)) of
+ true ->
+ Reason;
+ false ->
+ {'function not exported',[{M,F,A,L}|MFAs]}
+ end
+ end;
+fix_reason(Reason) ->
+ Reason.
+
+format_server_log_single([],_) ->
+ {"",[]};
+format_server_log_single(Log,FormatOpts) ->
+ Args =
+ case maps:get(depth,FormatOpts) of
+ unlimited ->
+ [Log];
+ Depth ->
+ [Log, Depth]
+ end,
+ {" Log: "++p(FormatOpts),Args}.
+
+format_client_log_single(undefined,_) ->
+ {"",[]};
+format_client_log_single({From,dead},_) ->
+ {" Client ~0p is dead.",[From]};
+format_client_log_single({From,remote},_) ->
+ {" Client ~0p is remote on node ~0p.", [From, node(From)]};
+format_client_log_single({_From,{Name,Stacktrace0}},FormatOpts) ->
+ P = p(FormatOpts),
+ %% Minimize the stacktrace a bit for single line reports. This is
+ %% hopefully enough to point out the position.
+ Stacktrace = lists:sublist(Stacktrace0,4),
+ Args =
+ case maps:get(depth,FormatOpts) of
+ unlimited ->
+ [Name, Stacktrace];
+ Depth ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {" Client "++P++" stacktrace: "++P++".", Args}.
+
+format_client_log(undefined,_) ->
{"", []};
-format_client_log({From,dead}) ->
+format_client_log({From,dead},_) ->
{"** Client ~p is dead~n", [From]};
-format_client_log({From,remote}) ->
+format_client_log({From,remote},_) ->
{"** Client ~p is remote on node ~p~n", [From, node(From)]};
-format_client_log({_From,{Name,Stacktrace}}) ->
- {"** Client ~tp stacktrace~n"
- "** ~tp~n",
- [Name, error_logger:limit_term(Stacktrace)]}.
+format_client_log({_From,{Name,Stacktrace}},FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["** Client ",P," stacktrace~n",
+ "** ",P,"~n"]),
+ Args =
+ case maps:get(depth,FormatOpts) of
+ unlimited ->
+ [Name, Stacktrace];
+ Depth ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format,Args}.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
%%-----------------------------------------------------------------
%% Status information
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 2cf30e10ec..adf1a82a08 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -21,11 +21,18 @@
-include("logger.hrl").
+%%%
+%%% NOTE: If init_ack() return values are modified, see comment
+%%% above monitor_return() in gen.erl!
+%%%
+
%% API
-export(
[start/3,start/4,start_link/3,start_link/4,
+ start_monitor/3,start_monitor/4,
stop/1,stop/3,
cast/2,call/2,call/3,
+ send_request/2,wait_response/1,wait_response/2,check_response/2,
enter_loop/4,enter_loop/5,enter_loop/6,
reply/1,reply/2]).
@@ -47,18 +54,20 @@
[wakeup_from_hibernate/3]).
%% logger callback
--export([format_log/1]).
+-export([format_log/1, format_log/2]).
%% Type exports for templates and callback modules
-export_type(
[event_type/0,
+ from/0,
callback_mode_result/0,
init_result/1,
state_enter_result/1,
event_handler_result/1,
reply_action/0,
enter_action/0,
- action/0]).
+ action/0
+ ]).
%% Old types, not advertised
-export_type(
[state_function_result/0,
@@ -259,6 +268,7 @@
Replies :: [reply_action()] | reply_action(),
NewData :: data()}.
+-type request_id() :: term().
%% The state machine init function. It is called only once and
%% the server is not running until this function has returned
@@ -455,12 +465,16 @@ timeout_event_type(Type) ->
| {'via', RegMod :: module(), ViaName :: term()}.
-type start_opt() ::
{'timeout', Time :: timeout()}
- | {'spawn_opt', [proc_lib:spawn_option()]}
+ | {'spawn_opt', [proc_lib:start_spawn_option()]}
| enter_loop_opt().
-type start_ret() ::
{'ok', pid()}
| 'ignore'
| {'error', term()}.
+-type start_mon_ret() ::
+ {'ok', {pid(),reference()}}
+ | 'ignore'
+ | {'error', term()}.
-type enter_loop_opt() ::
{'hibernate_after', HibernateAfterTimeout :: timeout()}
| {'debug', Dbgs :: [sys:debug_option()]}.
@@ -495,6 +509,20 @@ start_link(Module, Args, Opts) ->
start_link(ServerName, Module, Args, Opts) ->
gen:start(?MODULE, link, ServerName, Module, Args, Opts).
+%% Start and monitor a state machine
+-spec start_monitor(
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_mon_ret().
+start_monitor(Module, Args, Opts) ->
+ gen:start(?MODULE, monitor, Module, Args, Opts).
+%%
+-spec start_monitor(
+ ServerName :: server_name(),
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_mon_ret().
+start_monitor(ServerName, Module, Args, Opts) ->
+ gen:start(?MODULE, monitor, ServerName, Module, Args, Opts).
+
%% Stop a state machine
-spec stop(ServerRef :: server_ref()) -> ok.
stop(ServerRef) ->
@@ -553,6 +581,26 @@ call(ServerRef, Request, {_, _} = Timeout) ->
call(ServerRef, Request, Timeout) ->
call_clean(ServerRef, Request, Timeout, Timeout).
+-spec send_request(ServerRef::server_ref(), Request::term()) ->
+ RequestId::request_id().
+send_request(Name, Request) ->
+ gen:send_request(Name, '$gen_call', Request).
+
+-spec wait_response(RequestId::request_id()) ->
+ {reply, Reply::term()} | {error, {term(), server_ref()}}.
+wait_response(RequestId) ->
+ gen:wait_response(RequestId, infinity).
+
+-spec wait_response(RequestId::request_id(), timeout()) ->
+ {reply, Reply::term()} | 'timeout' | {error, {term(), server_ref()}}.
+wait_response(RequestId, Timeout) ->
+ gen:wait_response(RequestId, Timeout).
+
+-spec check_response(Msg::term(), RequestId::request_id()) ->
+ {reply, Reply::term()} | 'no_reply' | {error, {term(), server_ref()}}.
+check_response(Msg, RequestId) ->
+ gen:check_response(Msg, RequestId).
+
%% Reply from a state machine callback to whom awaits in call/2
-spec reply([reply_action()] | reply_action()) -> ok.
reply({reply,From,Reply}) ->
@@ -2365,8 +2413,10 @@ error_info(
reason=>{Class,Reason,Stacktrace},
client_info=>client_stacktrace(Q)},
#{domain=>[otp],
- report_cb=>fun gen_statem:format_log/1,
- error_logger=>#{tag=>error}}).
+ report_cb=>fun gen_statem:format_log/2,
+ error_logger=>
+ #{tag=>error,
+ report_cb=>fun gen_statem:format_log/1}}).
client_stacktrace([]) ->
undefined;
@@ -2392,43 +2442,158 @@ client_stacktrace([_|_]) ->
undefined.
-format_log(#{label:={gen_statem,terminate},
- name:=Name,
- queue:=Q,
- postponed:=Postponed,
- modules:=Modules,
- callback_mode:=CallbackMode,
- state_enter:=StateEnter,
- state:=FmtData,
- timeouts:=Timeouts,
- log:=Log,
- reason:={Class,Reason,Stacktrace},
- client_info:=ClientInfo}) ->
- {FixedReason,FixedStacktrace} =
- case Stacktrace of
- [{M,F,Args,_}|ST]
- when Class =:= error, Reason =:= undef ->
- case code:is_loaded(M) of
- false ->
- {{'module could not be loaded',M},ST};
- _ ->
- Arity =
- if
- is_list(Args) ->
- length(Args);
- is_integer(Args) ->
- Args
- end,
- case erlang:function_exported(M, F, Arity) of
- true ->
- {Reason,Stacktrace};
- false ->
- {{'function not exported',{M,F,Arity}},ST}
- end
- end;
- _ -> {Reason,Stacktrace}
- end,
- {ClientFmt,ClientArgs} = format_client_log(ClientInfo),
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(Report) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(Report, Depth), FormatOpts).
+
+limit_report(Report, unlimited) ->
+ Report;
+limit_report(#{label:={gen_statem,terminate},
+ queue:=Q,
+ postponed:=Postponed,
+ modules:=Modules,
+ state:=FmtData,
+ timeouts:=Timeouts,
+ log:=Log,
+ reason:={Class,Reason,Stacktrace},
+ client_info:=ClientInfo}=Report,
+ Depth) ->
+ Report#{queue =>
+ case Q of
+ [Event|Events] ->
+ [io_lib:limit_term(Event, Depth)
+ |io_lib:limit_term(Events, Depth)];
+ _ -> []
+ end,
+ postponed =>
+ case Postponed of
+ [] -> [];
+ _ -> io_lib:limit_term(Postponed, Depth)
+ end,
+ modules => io_lib:limit_term(Modules, Depth),
+ state => io_lib:limit_term(FmtData, Depth),
+ timeouts =>
+ case Timeouts of
+ {0,_} -> Timeouts;
+ _ -> io_lib:limit_term(Timeouts, Depth)
+ end,
+ log =>
+ case Log of
+ [] -> [];
+ _ -> [io_lib:limit_term(T, Depth) || T <- Log]
+ end,
+ reason =>
+ {Class,
+ io_lib:limit_term(Reason, Depth),
+ io_lib:limit_term(Stacktrace, Depth)},
+ client_info => limit_client_info(ClientInfo, Depth)}.
+
+
+limit_client_info({Pid,{Name,Stacktrace}}, Depth) ->
+ {Pid,{Name,io_lib:limit_term(Stacktrace, Depth)}};
+limit_client_info(Client, _Depth) ->
+ Client.
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default,FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={gen_statem,terminate},
+ name:=Name,
+ queue:=Q,
+ %% postponed
+ %% callback_mode
+ %% state_enter
+ state:=FmtData,
+ %% timeouts
+ log:=Log,
+ reason:={Class,Reason,Stacktrace},
+ client_info:=ClientInfo},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ {FixedReason,FixedStacktrace} = fix_reason(Class, Reason, Stacktrace),
+ {ClientFmt,ClientArgs} = format_client_log_single(ClientInfo, P, Depth),
+ Format =
+ lists:append(
+ ["State machine ",P," terminating. Reason: ",P,
+ case FixedStacktrace of
+ [] -> "";
+ _ -> ". Stack: "++P
+ end,
+ case Q of
+ [] -> "";
+ _ -> ". Last event: "++P
+ end,
+ ". State: ",P,
+ case Log of
+ [] -> "";
+ _ -> ". Log: "++P
+ end,
+ "."]),
+ Args0 =
+ [Name,FixedReason] ++
+ case FixedStacktrace of
+ [] -> [];
+ _ -> [FixedStacktrace]
+ end ++
+ case Q of
+ [] -> [];
+ [Event|_] -> [Event]
+ end ++
+ [FmtData] ++
+ case Log of
+ [] -> [];
+ _ -> [Log]
+ end,
+ Args = case Depth of
+ unlimited ->
+ Args0;
+ _ ->
+ lists:flatmap(fun(A) -> [A, Depth] end, Args0)
+ end,
+ {Format++ClientFmt, Args++ClientArgs};
+format_log_single(Report, FormatOpts) ->
+ format_log_multi(Report, FormatOpts).
+
+format_log_multi(#{label:={gen_statem,terminate},
+ name:=Name,
+ queue:=Q,
+ postponed:=Postponed,
+ modules:=Modules,
+ callback_mode:=CallbackMode,
+ state_enter:=StateEnter,
+ state:=FmtData,
+ timeouts:=Timeouts,
+ log:=Log,
+ reason:={Class,Reason,Stacktrace},
+ client_info:=ClientInfo},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ {FixedReason,FixedStacktrace} = fix_reason(Class, Reason, Stacktrace),
+ {ClientFmt,ClientArgs} = format_client_log(ClientInfo, P, Depth),
CBMode =
case StateEnter of
true ->
@@ -2436,76 +2601,147 @@ format_log(#{label:={gen_statem,terminate},
false ->
CallbackMode
end,
- {"** State machine ~tp terminating~n" ++
- case Q of
- [] -> "";
- _ -> "** Last event = ~tp~n"
- end ++
- "** When server state = ~tp~n" ++
- "** Reason for termination = ~w:~tp~n" ++
- "** Callback modules = ~p~n" ++
- "** Callback mode = ~p~n" ++
+ Format =
+ lists:append(
+ ["** State machine ",P," terminating~n",
+ case Q of
+ [] -> "";
+ _ -> "** Last event = "++P++"~n"
+ end,
+ "** When server state = ",P,"~n",
+ "** Reason for termination = ",P,":",P,"~n",
+ "** Callback modules = ",P,"~n",
+ "** Callback mode = ",P,"~n",
+ case Q of
+ [_,_|_] -> "** Queued = "++P++"~n";
+ _ -> ""
+ end,
+ case Postponed of
+ [] -> "";
+ _ -> "** Postponed = "++P++"~n"
+ end,
+ case FixedStacktrace of
+ [] -> "";
+ _ -> "** Stacktrace =~n** "++P++"~n"
+ end,
+ case Timeouts of
+ {0,_} -> "";
+ _ -> "** Time-outs: "++P++"~n"
+ end,
+ case Log of
+ [] -> "";
+ _ -> "** Log =~n** "++P++"~n"
+ end]),
+ Args0 =
+ [Name |
case Q of
- [_,_|_] -> "** Queued = ~tp~n";
- _ -> ""
- end ++
- case Postponed of
- [] -> "";
- _ -> "** Postponed = ~tp~n"
- end ++
- case FixedStacktrace of
- [] -> "";
- _ -> "** Stacktrace =~n** ~tp~n"
- end ++
- case Timeouts of
- {0,_} -> "";
- _ -> "** Time-outs: ~p~n"
- end ++
- case Log of
- [] -> "";
- _ -> "** Log =~n** ~tp~n"
- end ++ ClientFmt,
- [Name |
- case Q of
- [] -> [];
- [Event|_] -> [error_logger:limit_term(Event)]
- end] ++
- [error_logger:limit_term(FmtData),
- Class,error_logger:limit_term(FixedReason),
- error_logger:limit_term(Modules),
- CBMode] ++
- case Q of
- [_|[_|_] = Events] -> [error_logger:limit_term(Events)];
- _ -> []
- end ++
- case Postponed of
- [] -> [];
- _ -> [error_logger:limit_term(Postponed)]
- end ++
- case FixedStacktrace of
[] -> [];
- _ -> [error_logger:limit_term(FixedStacktrace)]
- end ++
- case Timeouts of
- {0,_} -> [];
- _ -> [error_logger:limit_term(Timeouts)]
- end ++
- case Log of
- [] -> [];
- _ -> [[error_logger:limit_term(T) || T <- Log]]
- end ++ ClientArgs}.
+ [Event|_] -> [Event]
+ end] ++
+ [FmtData,
+ Class,FixedReason,
+ Modules,
+ CBMode] ++
+ case Q of
+ [_|[_|_] = Events] -> [Events];
+ _ -> []
+ end ++
+ case Postponed of
+ [] -> [];
+ _ -> [Postponed]
+ end ++
+ case FixedStacktrace of
+ [] -> [];
+ _ -> [FixedStacktrace]
+ end ++
+ case Timeouts of
+ {0,_} -> [];
+ _ -> [Timeouts]
+ end ++
+ case Log of
+ [] -> [];
+ _ -> [Log]
+ end,
+ Args = case Depth of
+ unlimited ->
+ Args0;
+ _ ->
+ lists:flatmap(fun(A) -> [A, Depth] end, Args0)
+ end,
+ {Format++ClientFmt,Args++ClientArgs}.
+
+fix_reason(Class, Reason, Stacktrace) ->
+ case Stacktrace of
+ [{M,F,Args,_}|ST]
+ when Class =:= error, Reason =:= undef ->
+ case code:is_loaded(M) of
+ false ->
+ {{'module could not be loaded',M},ST};
+ _ ->
+ Arity =
+ if
+ is_list(Args) ->
+ length(Args);
+ is_integer(Args) ->
+ Args
+ end,
+ case erlang:function_exported(M, F, Arity) of
+ true ->
+ {Reason,Stacktrace};
+ false ->
+ {{'function not exported',{M,F,Arity}},ST}
+ end
+ end;
+ _ -> {Reason,Stacktrace}
+ end.
-format_client_log(undefined) ->
+format_client_log_single(undefined, _, _) ->
{"", []};
-format_client_log({Pid,dead}) ->
+format_client_log_single({Pid,dead}, _, _) ->
+ {" Client ~0p is dead.", [Pid]};
+format_client_log_single({Pid,remote}, _, _) ->
+ {" Client ~0p is remote on node ~0p.", [Pid,node(Pid)]};
+format_client_log_single({_Pid,{Name,Stacktrace0}}, P, Depth) ->
+ %% Minimize the stacktrace a bit for single line reports. This is
+ %% hopefully enough to point out the position.
+ Stacktrace = lists:sublist(Stacktrace0, 4),
+ Format = lists:append([" Client ",P," stacktrace: ",P,"."]),
+ Args = case Depth of
+ unlimited ->
+ [Name, Stacktrace];
+ _ ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format, Args}.
+
+format_client_log(undefined, _, _) ->
+ {"", []};
+format_client_log({Pid,dead}, _, _) ->
{"** Client ~p is dead~n", [Pid]};
-format_client_log({Pid,remote}) ->
- {"** Client ~p is remote on node ~p~n", [Pid, node(Pid)]};
-format_client_log({_Pid,{Name,Stacktrace}}) ->
- {"** Client ~tp stacktrace~n"
- "** ~tp~n",
- [Name, error_logger:limit_term(Stacktrace)]}.
-
+format_client_log({Pid,remote}, _, _) ->
+ {"** Client ~p is remote on node ~p~n", [Pid,node(Pid)]};
+format_client_log({_Pid,{Name,Stacktrace}}, P, Depth) ->
+ Format = lists:append(["** Client ",P," stacktrace~n** ",P,"~n"]),
+ Args = case Depth of
+ unlimited ->
+ [Name, Stacktrace];
+ _ ->
+ [Name, Depth, Stacktrace, Depth]
+ end,
+ {Format,Args}.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
%% Call Module:format_status/2 or return a default value
format_status(
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index 63c9a6bddf..1848aa3628 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -106,7 +106,6 @@ nl() ->
IoDevice :: device().
nl(Io) ->
-% o_request(Io, {put_chars,io_lib:nl()}).
o_request(Io, nl, nl).
-spec columns() -> {'ok', pos_integer()} | {'error', 'enotsup'}.
@@ -255,8 +254,6 @@ read(Io, Prompt) ->
case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of
{ok,Toks,_EndLine} ->
erl_parse:parse_term(Toks);
-% {error, Reason} when atom(Reason) ->
-% erlang:error(conv_reason(read, Reason), [Io, Prompt]);
{error,E,_EndLine} ->
{error,E};
{eof,_EndLine} ->
@@ -352,12 +349,7 @@ fread(Prompt, Format) ->
| server_no_data().
fread(Io, Prompt, Format) ->
- case request(Io, {fread,Prompt,Format}) of
-% {error, Reason} when atom(Reason) ->
-% erlang:error(conv_reason(fread, Reason), [Io, Prompt, Format]);
- Other ->
- Other
- end.
+ request(Io, {fread,Prompt,Format}).
-spec format(Format) -> 'ok' when
Format :: format().
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 21d66c5529..e2823b70f2 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -78,7 +78,7 @@
%% Utilities for collecting characters.
-export([collect_chars/3, collect_chars/4,
- collect_line/2, collect_line/3, collect_line/4,
+ collect_line/3, collect_line/4,
get_until/3, get_until/4]).
%% The following functions were used by Yecc's include-file.
@@ -851,6 +851,7 @@ collect_chars({binary,Stack,N}, Data,latin1, _) ->
end;
collect_chars({list,Stack,N}, Data, _,_) ->
collect_chars_list(Stack, N, Data);
+
%% collect_chars(Continuation, MoreChars, Count)
%% Returns:
%% {done,Result,RestChars}
@@ -881,32 +882,6 @@ collect_chars_list(Stack, N, []) ->
collect_chars_list(Stack,N, [H|T]) ->
collect_chars_list([H|Stack], N-1, T).
-%% collect_line(Continuation, MoreChars)
-%% Returns:
-%% {done,Result,RestChars}
-%% {more,Continuation}
-%%
-%% XXX Can be removed when compatibility with pre-R12B-5 nodes
-%% is no longer required.
-%%
-collect_line([], Chars) ->
- collect_line1(Chars, []);
-collect_line({SoFar}, More) ->
- collect_line1(More, SoFar).
-
-collect_line1([$\r, $\n|Rest], Stack) ->
- collect_line1([$\n|Rest], Stack);
-collect_line1([$\n|Rest], Stack) ->
- {done,lists:reverse([$\n|Stack], []),Rest};
-collect_line1([C|Rest], Stack) ->
- collect_line1(Rest, [C|Stack]);
-collect_line1(eof, []) ->
- {done,eof,[]};
-collect_line1(eof, Stack) ->
- {done,lists:reverse(Stack, []),[]};
-collect_line1([], Stack) ->
- {more,{Stack}}.
-
%% collect_line(State, Data, _). New in R9C.
%% Returns:
%% {stop,Result,RestData}
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index 77f02eafe0..838d412d0c 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -895,9 +895,6 @@ write_string(S, _Uni) ->
io_lib:write_string(S, $"). %"
expand({_, _, _Dots=0, no_more} = If, _T, _Dd) -> If;
-%% expand({{list,L}, _Len, _, no_more}, T, Dd) ->
-%% {NL, NLen, NDots} = expand_list(L, T, Dd, 2),
-%% {{list,NL}, NLen, NDots, no_more};
expand({{tuple,IsTagged,L}, _Len, _, no_more}, T, Dd) ->
{NL, NLen, NDots} = expand_list(L, T, Dd, 2),
{{tuple,IsTagged,NL}, NLen, NDots, no_more};
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index 06c90c0280..f44ed726ca 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -341,8 +341,12 @@ max([], Max) -> Max.
Len :: non_neg_integer(),
T :: term().
-sublist(List, S, L) when is_integer(L), L >= 0 ->
- sublist(nthtail(S-1, List), L).
+sublist(List, 1, L) when is_list(List), is_integer(L), L >= 0 ->
+ sublist(List, L);
+sublist([], S, _L) when is_integer(S), S >= 2 ->
+ [];
+sublist([_H|T], S, L) when is_integer(S), S >= 2 ->
+ sublist(T, S-1, L).
-spec sublist(List1, Len) -> List2 when
List1 :: [T],
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 51965ddb57..49d6a12eb2 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -100,6 +100,7 @@ merge(_,_) -> erlang:nif_error(undef).
put(_,_,_) -> erlang:nif_error(undef).
+%% Shadowed by erl_bif_types: maps:remove/2
-spec remove(Key,Map1) -> Map2 when
Key :: term(),
Map1 :: map(),
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index 2024be53bd..1cc11d9093 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -1,574 +1,520 @@
%%
-%% %CopyrightBegin%
+%% WARNING: DO NOT EDIT THIS FILE.
%%
-%% Copyright Ericsson AB 1999-2020. All Rights Reserved.
+%% This file was auto-generated from attributes in the source
+%% code.
%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
+%% To add a description to a deprecation or removal attribute,
+%% write a string after the arity:
%%
-%% http://www.apache.org/licenses/LICENSE-2.0
+%% -deprecated([{foo,1,"use bar/1 instead"}]).
+%% -deprecated_type([{gadget,1,"use widget/1 instead"}]).
+%% -removed([{hello,2,"use there/2 instead"}]).
+%% -removed_type([{frobnitz,1,"use grunka/1 instead"}]).
%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
+%% Descriptions cannot be given with the `f/1` shorthand, and
+%% it will fall back to a generic description referring the
+%% user to the documentation.
%%
-%% %CopyrightEnd%
+%% Use `./otp_build update_deprecations` to update this file
+%% after adding an attribute.
%%
-module(otp_internal).
-
--export([obsolete/3, obsolete_type/3]).
-
-%%----------------------------------------------------------------------
-
+-include("otp_internal.hrl").
+%%
-dialyzer({no_match, obsolete/3}).
-
--type tag() :: 'deprecated' | 'removed'. %% | 'experimental'.
--type mfas() :: mfa() | {atom(), atom(), [byte()]}.
--type release() :: string().
-
--spec obsolete(module(), atom(), arity()) ->
- 'no' | {tag(), string()} | {tag(), mfas(), release()}.
-
-obsolete(Module, Name, Arity) ->
- case obsolete_1(Module, Name, Arity) of
- {deprecated=Tag,{_,_,_}=Replacement} ->
- {Tag,Replacement,"a future release"};
- {_,String}=Ret when is_list(String) ->
- Ret;
- {_,_,_}=Ret ->
- Ret;
- no ->
- no
- end.
-
-obsolete_1(net, call, 4) ->
- {deprecated, {rpc, call, 4}};
-obsolete_1(net, cast, 4) ->
- {deprecated, {rpc, cast, 4}};
-obsolete_1(net, broadcast, 3) ->
- {deprecated, {rpc, eval_everywhere, 3}};
-obsolete_1(net, ping, 1) ->
- {deprecated, {net_adm, ping, 1}};
-obsolete_1(net, sleep, 1) ->
- {deprecated, "Use 'receive after T -> ok end' instead"};
-obsolete_1(net, relay, 1) ->
- {deprecated, {slave, relay, 1}};
-
-
-obsolete_1(erlang, now, 0) ->
- {deprecated,
- "Deprecated BIF. See the \"Time and Time Correction in Erlang\" "
- "chapter of the ERTS User's Guide for more information."};
-
-obsolete_1(calendar, local_time_to_universal_time, 1) ->
- {deprecated, {calendar, local_time_to_universal_time_dst, 1}};
-
-%% *** STDLIB added in OTP 22 ***
-
-obsolete_1(sys, get_debug, 3) ->
- {deprecated,
- "Deprecated function. "
- "Incorrectly documented and in fact only for internal use. "
- "Can often be replaced with sys:get_log/1."};
-
-%% *** STDLIB added in OTP 20 ***
-
-obsolete_1(gen_fsm, start, 3) ->
- {deprecated, {gen_statem, start, 3}};
-obsolete_1(gen_fsm, start, 4) ->
- {deprecated, {gen_statem, start, 4}};
-
-obsolete_1(gen_fsm, start_link, 3) ->
- {deprecated, {gen_statem, start_link, 3}};
-obsolete_1(gen_fsm, start_link, 4) ->
- {deprecated, {gen_statem, start_link, 4}};
-
-obsolete_1(gen_fsm, stop, 1) ->
- {deprecated, {gen_statem, stop, 1}};
-obsolete_1(gen_fsm, stop, 3) ->
- {deprecated, {gen_statem, stop, 3}};
-
-obsolete_1(gen_fsm, enter_loop, 4) ->
- {deprecated, {gen_statem, enter_loop, 4}};
-obsolete_1(gen_fsm, enter_loop, 5) ->
- {deprecated, {gen_statem, enter_loop, 5}};
-obsolete_1(gen_fsm, enter_loop, 6) ->
- {deprecated, {gen_statem, enter_loop, 6}};
-
-obsolete_1(gen_fsm, reply, 2) ->
- {deprecated, {gen_statem, reply, 2}};
-
-obsolete_1(gen_fsm, send_event, 2) ->
- {deprecated, {gen_statem, cast, 2}};
-obsolete_1(gen_fsm, send_all_state_event, 2) ->
- {deprecated, {gen_statem, cast, 2}};
-
-obsolete_1(gen_fsm, sync_send_event, 2) ->
- {deprecated, {gen_statem, call, 2}};
-obsolete_1(gen_fsm, sync_send_event, 3) ->
- {deprecated, {gen_statem, call, 3}};
-
-obsolete_1(gen_fsm, sync_send_all_state_event, 2) ->
- {deprecated, {gen_statem, call, 2}};
-obsolete_1(gen_fsm, sync_send_all_state_event, 3) ->
- {deprecated, {gen_statem, call, 3}};
-
-obsolete_1(gen_fsm, start_timer, 2) ->
- {deprecated, {erlang, start_timer, 3}};
-obsolete_1(gen_fsm, cancel_timer, 1) ->
- {deprecated, {erlang, cancel_timer, 1}};
-obsolete_1(gen_fsm, send_event_after, 2) ->
- {deprecated, {erlang, send_after, 3}};
-
-%% *** CRYPTO added in OTP 20 ***
-
-obsolete_1(crypto, rand_uniform, 2) ->
- {deprecated, {rand, uniform, 1}};
-
-%% *** CRYPTO added in OTP 19 ***
-
-obsolete_1(crypto, rand_bytes, 1) ->
- {removed, {crypto, strong_rand_bytes, 1}, "20.0"};
-
-%% *** CRYPTO added in R16B01 ***
-
-obsolete_1(crypto, md4, 1) ->
- {removed, {crypto, hash, 2}, "20.0"};
-obsolete_1(crypto, md5, 1) ->
- {removed, {crypto, hash, 2}, "20.0"};
-obsolete_1(crypto, sha, 1) ->
- {removed, {crypto, hash, 2}, "20.0"};
-
-obsolete_1(crypto, md4_init, 0) ->
- {removed, {crypto, hash_init, 1}, "20.0"};
-obsolete_1(crypto, md5_init, 0) ->
- {removed, {crypto, hash_init, 1}, "20.0"};
-obsolete_1(crypto, sha_init, 0) ->
- {removed, {crypto, hash_init, 1}, "20.0"};
-
-obsolete_1(crypto, md4_update, 2) ->
- {removed, {crypto, hash_update, 2}, "20.0"};
-obsolete_1(crypto, md5_update, 2) ->
- {removed, {crypto, hash_update, 2}, "20.0"};
-obsolete_1(crypto, sha_update, 2) ->
- {removed, {crypto, hash_update, 2}, "20.0"};
-
-obsolete_1(crypto, md4_final, 1) ->
- {removed, {crypto, hash_final, 1}, "20.0"};
-obsolete_1(crypto, md5_final, 1) ->
- {removed, {crypto, hash_final, 1}, "20.0"};
-obsolete_1(crypto, sha_final, 1) ->
- {removed, {crypto, hash_final, 1}, "20.0"};
-
-obsolete_1(crypto, md5_mac, 2) ->
- {removed, {crypto, hmac, 3}, "20.0"};
-obsolete_1(crypto, sha_mac, 2) ->
- {removed, {crypto, hmac, 3}, "20.0"};
-obsolete_1(crypto, sha_mac, 3) ->
- {removed, {crypto, hmac, 4}, "20.0"};
-
-obsolete_1(crypto, sha_mac_96, 2) ->
- {removed, {crypto, hmac, 4}, "20.0"};
-obsolete_1(crypto, md5_mac_96, 2) ->
- {removed, {crypto, hmac, 4}, "20.0"};
-
-obsolete_1(crypto, rsa_sign, 2) ->
- {removed, {crypto, sign, 4}, "20.0"};
-obsolete_1(crypto, rsa_sign, 3) ->
- {removed, {crypto, sign, 4}, "20.0"};
-obsolete_1(crypto, rsa_verify, 3) ->
- {removed, {crypto, verify, 5}, "20.0"};
-obsolete_1(crypto, rsa_verify, 4) ->
- {removed, {crypto, verify, 5}, "20.0"};
-
-obsolete_1(crypto, dss_sign, 2) ->
- {removed, {crypto, sign, 4}, "20.0"};
-obsolete_1(crypto, dss_sign, 3) ->
- {removed, {crypto, sign, 4}, "20.0"};
-
-obsolete_1(crypto, dss_verify, 3) ->
- {removed, {crypto, verify, 5}, "20.0"};
-obsolete_1(crypto, dss_verify, 4) ->
- {removed, {crypto, verify, 5}, "20.0"};
-
-obsolete_1(crypto, mod_exp, 3) ->
- {removed, {crypto, mod_pow, 3}, "20.0"};
-
-obsolete_1(crypto, dh_compute_key, 3) ->
- {removed, {crypto, compute_key, 4}, "20.0"};
-obsolete_1(crypto, dh_generate_key, 1) ->
- {removed, {crypto, generate_key, 2}, "20.0"};
-obsolete_1(crypto, dh_generate_key, 2) ->
- {removed, {crypto, generate_key, 3}, "20.0"};
-
-obsolete_1(crypto, des_cbc_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, des3_cbc_encrypt, 5) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, des_ecb_encrypt, 2) ->
- {removed, {crypto, block_encrypt, 3}, "20.0"};
-obsolete_1(crypto, des_ede3_cbc_encrypt, 5) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, des_cfb_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, des3_cfb_encrypt, 5) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_ecb_encrypt, 2) ->
- {removed, {crypto, block_encrypt, 3}, "20.0"};
-obsolete_1(crypto, blowfish_cbc_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_cfb64_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_ofb64_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cfb_128_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cbc_128_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cbc_256_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto,rc2_cbc_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-obsolete_1(crypto,rc2_40_cbc_encrypt, 3) ->
- {removed, {crypto, block_encrypt, 4}, "20.0"};
-
-obsolete_1(crypto, des_cbc_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, des3_cbc_decrypt, 5) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, des_ecb_decrypt, 2) ->
- {removed, {crypto, block_decrypt, 3}, "20.0"};
-obsolete_1(crypto, des_ede3_cbc_decrypt, 5) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, des_cfb_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, des3_cfb_decrypt, 5) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_ecb_decrypt, 2) ->
- {removed, {crypto, block_decrypt, 3}, "20.0"};
-obsolete_1(crypto, blowfish_cbc_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_cfb64_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, blowfish_ofb64_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cfb_128_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cbc_128_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto, aes_cbc_256_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto,rc2_cbc_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-obsolete_1(crypto,rc2_40_cbc_decrypt, 3) ->
- {removed, {crypto, block_decrypt, 4}, "20.0"};
-
-obsolete_1(crypto, aes_ctr_stream_decrypt, 2) ->
- {removed, {crypto, stream_decrypt, 2}, "20.0"};
-obsolete_1(crypto, aes_ctr_stream_encrypt, 2) ->
- {removed, {crypto, stream_encrypt, 2}, "20.0"};
-obsolete_1(crypto, aes_ctr_decrypt, 3) ->
- {removed, {crypto, stream_decrypt, 2}, "20.0"};
-obsolete_1(crypto, aes_ctr_encrypt, 3) ->
- {removed, {crypto, stream_encrypt, 2}, "20.0"};
-obsolete_1(crypto, rc4_encrypt, 2) ->
- {removed, {crypto, stream_encrypt, 2}, "20.0"};
-obsolete_1(crypto, rc4_encrypt_with_state, 2) ->
- {removed, {crypto, stream_encrypt, 2}, "20.0"};
-obsolete_1(crypto, aes_ctr_stream_init, 2) ->
- {removed, {crypto, stream_init, 3}, "20.0"};
-obsolete_1(crypto, rc4_set_key, 1) ->
- {removed, {crypto, stream_init, 2}, "20.0"};
-
-obsolete_1(crypto, rsa_private_decrypt, 3) ->
- {removed, {crypto, private_decrypt, 4}, "20.0"};
-obsolete_1(crypto, rsa_public_decrypt, 3) ->
- {removed, {crypto, public_decrypt, 4}, "20.0"};
-obsolete_1(crypto, rsa_private_encrypt, 3) ->
- {removed, {crypto, private_encrypt, 4}, "20.0"};
-obsolete_1(crypto, rsa_public_encrypt, 3) ->
- {removed, {crypto, public_encrypt, 4}, "20.0"};
-
-obsolete_1(crypto, des_cfb_ivec, 2) ->
- {removed, {crypto, next_iv, 3}, "20.0"};
-obsolete_1(crypto,des_cbc_ivec, 1) ->
- {removed, {crypto, next_iv, 2}, "20.0"};
-obsolete_1(crypto, aes_cbc_ivec, 1) ->
- {removed, {crypto, next_iv, 2}, "20.0"};
-
-obsolete_1(crypto,info, 0) ->
- {removed, {crypto, module_info, 0}, "20.0"};
-
-obsolete_1(crypto, strong_rand_mpint, 3) ->
- {removed, "removed in 20.0; only needed by removed functions"};
-obsolete_1(crypto, erlint, 1) ->
- {removed, "removed in 20.0; only needed by removed functions"};
-obsolete_1(crypto, mpint, 1) ->
- {removed, "removed in 20.0; only needed by removed functions"};
-
-
-%% *** SNMP ***
-
-obsolete_1(snmp, N, A) ->
- case is_snmp_agent_function(N, A) of
- false ->
- no;
- true ->
- {deprecated, "Deprecated (will be removed in OTP 18); use snmpa:"++atom_to_list(N)++"/"++
- integer_to_list(A)++" instead"}
- end;
-
-obsolete_1(snmpa, old_info_format, 1) ->
- {deprecated, "Deprecated; (will be removed in OTP 18); use \"new\" format instead"};
-
-
-%% *** MEGACO ***
-
-obsolete_1(megaco, format_versions, 1) ->
- {deprecated, "Deprecated; use megaco:print_version_info/0,1 instead"};
-
-
-%% *** OS-MON-MIB ***
-
-%% FIXME: Remove this warning in OTP 24.
-obsolete_1(os_mon_mib, _, _) ->
- {removed, "was removed in 22.0"};
-
-obsolete_1(auth, is_auth, 1) ->
- {deprecated, {net_adm, ping, 1}};
-obsolete_1(auth, cookie, 0) ->
- {deprecated, {erlang, get_cookie, 0}};
-obsolete_1(auth, cookie, 1) ->
- {deprecated, {erlang, set_cookie, 2}};
-obsolete_1(auth, node_cookie, 1) ->
- {deprecated, "Deprecated; use erlang:set_cookie/2 and net_adm:ping/1 instead"};
-obsolete_1(auth, node_cookie, 2) ->
- {deprecated, "Deprecated; use erlang:set_cookie/2 and net_adm:ping/1 instead"};
-
-%% Added in R16
-obsolete_1(wxCalendarCtrl, enableYearChange, _) -> %% wx bug documented?
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxDC, computeScaleAndOrigin, 1) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxClientDC, new, 0) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxPaintDC, new, 0) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxWindowDC, new, 0) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxGraphicsRenderer, createLinearGradientBrush, 7) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxGraphicsRenderer, createRadialGradientBrush, 8) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxGridCellEditor, endEdit, 4) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxGridCellEditor, paintBackground, 3) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxIdleEvent, canSend, 1) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxMDIClientWindow, new, 1) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxMDIClientWindow, new, 2) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxPostScriptDC, getResolution, 0) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxPostScriptDC, setResolution, 1) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxCursor, new, 3) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-obsolete_1(wxCursor, new, 4) ->
- {deprecated,"deprecated function not available in wxWidgets-2.9 and later"};
-
-%% Added in OTP 17.
-obsolete_1(asn1ct, decode,3) ->
- {removed,"removed; use Mod:decode/2 instead"};
-obsolete_1(asn1ct, encode, 2) ->
- {removed,"removed; use Mod:encode/2 instead"};
-obsolete_1(asn1ct, encode, 3) ->
- {removed,"removed; use Mod:encode/2 instead"};
-obsolete_1(asn1rt, decode,3) ->
- {removed,"removed; use Mod:decode/2 instead"};
-obsolete_1(asn1rt, encode, 2) ->
- {removed,"removed; use Mod:encode/2 instead"};
-obsolete_1(asn1rt, encode, 3) ->
- {removed,"removed; use Mod:encode/2 instead"};
-obsolete_1(asn1rt, info, 1) ->
- {removed,"removed; use Mod:info/0 instead"};
-obsolete_1(asn1rt, utf8_binary_to_list, 1) ->
- {removed,{unicode,characters_to_list,1},"OTP 20"};
-obsolete_1(asn1rt, utf8_list_to_binary, 1) ->
- {removed,{unicode,characters_to_binary,1},"OTP 20"};
-
-%% Added in OTP 18.
-obsolete_1(core_lib, get_anno, 1) ->
- {removed,{cerl,get_ann,1},"19"};
-obsolete_1(core_lib, set_anno, 2) ->
- {removed,{cerl,set_ann,2},"19"};
-obsolete_1(core_lib, is_literal, 1) ->
- {removed,{cerl,is_literal,1},"19"};
-obsolete_1(core_lib, is_literal_list, 1) ->
- {removed,"removed; use lists:all(fun cerl:is_literal/1, L)"
- " instead"};
-obsolete_1(core_lib, literal_value, 1) ->
- {removed,{core_lib,concrete,1},"19"};
-obsolete_1(erl_scan, set_attribute, 3) ->
- {removed,{erl_anno,set_line,2},"19.0"};
-obsolete_1(erl_scan, attributes_info, 1) ->
- {removed,"removed in 19.0; use "
- "erl_anno:{column,line,location,text}/1 instead"};
-obsolete_1(erl_scan, attributes_info, 2) ->
- {removed,"removed in 19.0; use "
- "erl_anno:{column,line,location,text}/1 instead"};
-obsolete_1(erl_scan, token_info, 1) ->
- {removed,"removed in 19.0; use "
- "erl_scan:{category,column,line,location,symbol,text}/1 instead"};
-obsolete_1(erl_scan, token_info, 2) ->
- {removed,"removed in 19.0; use "
- "erl_scan:{category,column,line,location,symbol,text}/1 instead"};
-obsolete_1(erl_parse, set_line, 2) ->
- {removed,{erl_anno,set_line,2},"19.0"};
-obsolete_1(erl_parse, get_attributes, 1) ->
- {removed,"removed in 19.0; use "
- "erl_anno:{column,line,location,text}/1 instead"};
-obsolete_1(erl_parse, get_attribute, 2) ->
- {removed,"removed in 19.0; use "
- "erl_anno:{column,line,location,text}/1 instead"};
-obsolete_1(erl_lint, modify_line, 2) ->
- {removed,{erl_parse,map_anno,2},"19.0"};
-obsolete_1(ssl, negotiated_next_protocol, 1) ->
- {removed,"removed in 20.0; use ssl:negotiated_protocol/1 instead"};
-obsolete_1(ssl, connection_info, 1) ->
- {removed, "removed in 20.0; use ssl:connection_information/[1,2] instead"};
-
-obsolete_1(httpd_conf, check_enum, 2) ->
- {deprecated, "deprecated; use lists:member/2 instead"};
-obsolete_1(httpd_conf, clean, 1) ->
- {deprecated, "deprecated; use sting:strip/1 instead or possible the re module"};
-obsolete_1(httpd_conf, custom_clean, 3) ->
- {deprecated, "deprecated; use sting:strip/3 instead or possible the re module"};
-obsolete_1(httpd_conf, is_directory, 1) ->
- {deprecated, "deprecated; use filelib:is_dir/1 instead"};
-obsolete_1(httpd_conf, is_file, 1) ->
- {deprecated, "deprecated; use filelib:is_file/1 instead"};
-obsolete_1(httpd_conf, make_integer, 1) ->
- {deprecated, "deprecated; use erlang:list_to_integer/1 instead"};
-
-%% Added in OTP 19.
-
-obsolete_1(random, _, _) ->
- {deprecated, "the 'random' module is deprecated; "
- "use the 'rand' module instead"};
-obsolete_1(code, rehash, 0) ->
- {deprecated, "deprecated because the code path cache feature has been removed"};
-obsolete_1(queue, lait, 1) ->
- {deprecated, {queue,liat,1}};
-
-%% Removed in OTP 19.
-
-obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 ->
- {removed, {rpc, multi_server_call, A}, "19.0"};
-
-%% Added in OTP 20.
-
-obsolete_1(filename, find_src, 1) ->
- {deprecated, "deprecated; use filelib:find_source/1 instead"};
-obsolete_1(filename, find_src, 2) ->
- {deprecated, "deprecated; use filelib:find_source/3 instead"};
-
-obsolete_1(erlang, get_stacktrace, 0) ->
- {deprecated, "deprecated; use the new try/catch syntax for retrieving the stack backtrace"};
-
-%% Removed in OTP 20.
-
-obsolete_1(erlang, hash, 2) ->
- {removed, {erlang, phash2, 2}, "20.0"};
-
-%% Add in OTP 21.
-
-obsolete_1(ssl, ssl_accept, 1) ->
- {deprecated, "deprecated; use ssl:handshake/1 instead"};
-obsolete_1(ssl, ssl_accept, 2) ->
- {deprecated, "deprecated; use ssl:handshake/2 instead"};
-obsolete_1(ssl, ssl_accept, 3) ->
- {deprecated, "deprecated; use ssl:handshake/3 instead"};
-obsolete_1(otp_mib, F, _) when F =:= load; F =:= unload ->
- {deprecated, "deprecated; functionality will be removed in a future release"};
-
-%% not obsolete
-
-obsolete_1(_, _, _) ->
- no.
-
--spec is_snmp_agent_function(atom(), byte()) -> boolean().
-
-is_snmp_agent_function(c, 1) -> true;
-is_snmp_agent_function(c, 2) -> true;
-is_snmp_agent_function(compile, 3) -> true;
-is_snmp_agent_function(is_consistent, 1) -> true;
-is_snmp_agent_function(mib_to_hrl, 1) -> true;
-is_snmp_agent_function(change_log_size, 1) -> true;
-is_snmp_agent_function(log_to_txt, 2) -> true;
-is_snmp_agent_function(log_to_txt, 3) -> true;
-is_snmp_agent_function(log_to_txt, 4) -> true;
-is_snmp_agent_function(current_request_id, 0) -> true;
-is_snmp_agent_function(current_community, 0) -> true;
-is_snmp_agent_function(current_address, 0) -> true;
-is_snmp_agent_function(current_context, 0) -> true;
-is_snmp_agent_function(current_net_if_data, 0) -> true;
-is_snmp_agent_function(get_symbolic_store_db, 0) -> true;
-is_snmp_agent_function(name_to_oid, 1) -> true;
-is_snmp_agent_function(name_to_oid, 2) -> true;
-is_snmp_agent_function(oid_to_name, 1) -> true;
-is_snmp_agent_function(oid_to_name, 2) -> true;
-is_snmp_agent_function(int_to_enum, 2) -> true;
-is_snmp_agent_function(int_to_enum, 3) -> true;
-is_snmp_agent_function(enum_to_int, 2) -> true;
-is_snmp_agent_function(enum_to_int, 3) -> true;
-is_snmp_agent_function(get, 2) -> true;
-is_snmp_agent_function(info, 1) -> true;
-is_snmp_agent_function(load_mibs, 2) -> true;
-is_snmp_agent_function(unload_mibs, 2) -> true;
-is_snmp_agent_function(dump_mibs, 0) -> true;
-is_snmp_agent_function(dump_mibs, 1) -> true;
-is_snmp_agent_function(register_subagent, 3) -> true;
-is_snmp_agent_function(unregister_subagent, 2) -> true;
-is_snmp_agent_function(send_notification, 3) -> true;
-is_snmp_agent_function(send_notification, 4) -> true;
-is_snmp_agent_function(send_notification, 5) -> true;
-is_snmp_agent_function(send_notification, 6) -> true;
-is_snmp_agent_function(send_trap, 3) -> true;
-is_snmp_agent_function(send_trap, 4) -> true;
-is_snmp_agent_function(add_agent_caps, 2) -> true;
-is_snmp_agent_function(del_agent_caps, 1) -> true;
-is_snmp_agent_function(get_agent_caps, 0) -> true;
-is_snmp_agent_function(_, _) -> false.
-
--dialyzer({no_match, obsolete_type/3}).
-
--spec obsolete_type(module(), atom(), arity()) ->
- 'no' | {tag(), string()} | {tag(), mfas(), release()}.
+obsolete(auth, cookie, 0) ->
+ {deprecated, "use erlang:get_cookie/0 instead"};
+obsolete(auth, cookie, 1) ->
+ {deprecated, "use erlang:set_cookie/2 instead"};
+obsolete(auth, is_auth, 1) ->
+ {deprecated, "use net_adm:ping/1 instead"};
+obsolete(calendar, local_time_to_universal_time, 1) ->
+ {deprecated, "use calendar:local_time_to_universal_time_dst/1 instead"};
+obsolete(code, rehash, 0) ->
+ {deprecated, "the code path cache feature has been removed"};
+obsolete(crypto, block_decrypt, 3) ->
+ {deprecated, "use crypto:crypto_one_time/4 or crypto:crypto_init/3 + crypto:crypto_update/2 + crypto:crypto_final/1 instead", "OTP 24"};
+obsolete(crypto, block_decrypt, 4) ->
+ {deprecated, "use crypto:crypto_one_time/5, crypto:crypto_one_time_aead/6,7 or crypto:crypto_(dyn_iv)?_init + crypto:crypto_(dyn_iv)?_update + crypto:crypto_final instead", "OTP 24"};
+obsolete(crypto, block_encrypt, 3) ->
+ {deprecated, "use crypto:crypto_one_time/4 or crypto:crypto_init/3 + crypto:crypto_update/2 + crypto:crypto_final/1 instead", "OTP 24"};
+obsolete(crypto, block_encrypt, 4) ->
+ {deprecated, "use crypto:crypto_one_time/5, crypto:crypto_one_time_aead/6,7 or crypto:crypto_(dyn_iv)?_init + crypto:crypto_(dyn_iv)?_update + crypto:crypto_final instead", "OTP 24"};
+obsolete(crypto, cmac, 3) ->
+ {deprecated, "use crypto:mac/4 instead", "OTP 24"};
+obsolete(crypto, cmac, 4) ->
+ {deprecated, "use crypto:macN/5 instead", "OTP 24"};
+obsolete(crypto, hmac, 3) ->
+ {deprecated, "use crypto:mac/4 instead", "OTP 24"};
+obsolete(crypto, hmac, 4) ->
+ {deprecated, "use crypto:macN/5 instead", "OTP 24"};
+obsolete(crypto, hmac_final, 1) ->
+ {deprecated, "use crypto:mac_final/1 instead", "OTP 24"};
+obsolete(crypto, hmac_final_n, 2) ->
+ {deprecated, "use crypto:mac_finalN/2 instead", "OTP 24"};
+obsolete(crypto, hmac_init, 2) ->
+ {deprecated, "use crypto:mac_init/3 instead", "OTP 24"};
+obsolete(crypto, hmac_update, 2) ->
+ {deprecated, "use crypto:mac_update/2 instead", "OTP 24"};
+obsolete(crypto, poly1305, 2) ->
+ {deprecated, "use crypto:mac/3 instead", "OTP 24"};
+obsolete(crypto, rand_uniform, 2) ->
+ {deprecated, "use rand:uniform/1 instead"};
+obsolete(crypto, stream_decrypt, 2) ->
+ {deprecated, "use crypto:crypto_update/2 instead", "OTP 24"};
+obsolete(crypto, stream_encrypt, 2) ->
+ {deprecated, "use crypto:crypto_update/2 instead", "OTP 24"};
+obsolete(erlang, get_stacktrace, 0) ->
+ {deprecated, "use the new try/catch syntax for retrieving the stack backtrace", "OTP 24"};
+obsolete(erlang, now, 0) ->
+ {deprecated, "see the \"Time and Time Correction in Erlang\" chapter of the ERTS User's Guide for more information"};
+obsolete(filename, safe_relative_path, 1) ->
+ {deprecated, "use filelib:safe_relative_path/2 instead", "OTP 25"};
+obsolete(http_uri, decode, 1) ->
+ {deprecated, "use uri_string functions instead", "OTP 25"};
+obsolete(http_uri, encode, 1) ->
+ {deprecated, "use uri_string functions instead", "OTP 25"};
+obsolete(http_uri, parse, 1) ->
+ {deprecated, "use uri_string functions instead", "OTP 25"};
+obsolete(http_uri, parse, 2) ->
+ {deprecated, "use uri_string functions instead", "OTP 25"};
+obsolete(http_uri, scheme_defaults, 0) ->
+ {deprecated, "use uri_string functions instead", "OTP 25"};
+obsolete(httpd, parse_query, 1) ->
+ {deprecated, "use uri_string:dissect_query/1 instead"};
+obsolete(megaco, format_versions, 1) ->
+ {deprecated, "use megaco:print_version_info/0,1 instead.", "OTP 24"};
+obsolete(net, broadcast, 3) ->
+ {deprecated, "use rpc:eval_everywhere/3 instead"};
+obsolete(net, call, 4) ->
+ {deprecated, "use rpc:call/4 instead"};
+obsolete(net, cast, 4) ->
+ {deprecated, "use rpc:cast/4 instead"};
+obsolete(net, ping, 1) ->
+ {deprecated, "use net_adm:ping/1 instead"};
+obsolete(net, relay, 1) ->
+ {deprecated, "use slave:relay/1 instead"};
+obsolete(net, sleep, 1) ->
+ {deprecated, "use 'receive after T -> ok end' instead"};
+obsolete(queue, lait, 1) ->
+ {deprecated, "use queue:liat/1 instead"};
+obsolete(snmp, add_agent_caps, 2) ->
+ {deprecated, "use snmpa:add_agent_caps/2 instead.", "OTP 24"};
+obsolete(snmp, c, 1) ->
+ {deprecated, "use snmpc:compile/1 instead.", "OTP 24"};
+obsolete(snmp, c, 2) ->
+ {deprecated, "use snmpc:compile/2 instead.", "OTP 24"};
+obsolete(snmp, change_log_size, 1) ->
+ {deprecated, "use snmpa:change_log_size/1 instead.", "OTP 24"};
+obsolete(snmp, compile, 3) ->
+ {deprecated, "use snmpc:compile/3 instead.", "OTP 24"};
+obsolete(snmp, current_address, 0) ->
+ {deprecated, "use snmpa:current_address/0 instead.", "OTP 24"};
+obsolete(snmp, current_community, 0) ->
+ {deprecated, "use snmpa:current_community/0 instead.", "OTP 24"};
+obsolete(snmp, current_context, 0) ->
+ {deprecated, "use snmpa:current_context/0 instead.", "OTP 24"};
+obsolete(snmp, current_net_if_data, 0) ->
+ {deprecated, "use snmpa:current_net_if_data/0 instead.", "OTP 24"};
+obsolete(snmp, current_request_id, 0) ->
+ {deprecated, "use snmpa:current_request_id/0 instead.", "OTP 24"};
+obsolete(snmp, del_agent_caps, 1) ->
+ {deprecated, "use snmpa:del_agent_caps/1 instead.", "OTP 24"};
+obsolete(snmp, dump_mibs, 0) ->
+ {deprecated, "use snmpa:dump_mibs/0 instead.", "OTP 24"};
+obsolete(snmp, dump_mibs, 1) ->
+ {deprecated, "use snmpa:dump_mibs/1 instead.", "OTP 24"};
+obsolete(snmp, enum_to_int, 2) ->
+ {deprecated, "use snmpa:enum_to_int/2 instead.", "OTP 24"};
+obsolete(snmp, enum_to_int, 3) ->
+ {deprecated, "use snmpa:enum_to_int/3 instead.", "OTP 24"};
+obsolete(snmp, get, 2) ->
+ {deprecated, "use snmpa:get/2 instead.", "OTP 24"};
+obsolete(snmp, get_agent_caps, 0) ->
+ {deprecated, "use snmpa:get_agent_caps/0 instead.", "OTP 24"};
+obsolete(snmp, get_symbolic_store_db, 0) ->
+ {deprecated, "use snmpa:get_symbolic_store_db/0 instead.", "OTP 24"};
+obsolete(snmp, info, 1) ->
+ {deprecated, "use snmpa:info/1 instead.", "OTP 24"};
+obsolete(snmp, int_to_enum, 2) ->
+ {deprecated, "use snmpa:int_to_enum/2 instead.", "OTP 24"};
+obsolete(snmp, int_to_enum, 3) ->
+ {deprecated, "use snmpa:int_to_enum/3 instead.", "OTP 24"};
+obsolete(snmp, is_consistent, 1) ->
+ {deprecated, "use snmpc:is_consistent/1 instead.", "OTP 24"};
+obsolete(snmp, load_mibs, 2) ->
+ {deprecated, "use snmpa:load_mibs/2 instead.", "OTP 24"};
+obsolete(snmp, log_to_txt, 2) ->
+ {deprecated, "use snmpa:log_to_txt/2 instead.", "OTP 24"};
+obsolete(snmp, log_to_txt, 3) ->
+ {deprecated, "use snmpa:log_to_txt/3 instead.", "OTP 24"};
+obsolete(snmp, log_to_txt, 4) ->
+ {deprecated, "use snmpa:log_to_txt/4 instead.", "OTP 24"};
+obsolete(snmp, mib_to_hrl, 1) ->
+ {deprecated, "use snmpc:mib_to_hrl/1 instead.", "OTP 24"};
+obsolete(snmp, name_to_oid, 1) ->
+ {deprecated, "use snmpa:name_to_oid/1 instead.", "OTP 24"};
+obsolete(snmp, name_to_oid, 2) ->
+ {deprecated, "use snmpa:name_to_oid/2 instead.", "OTP 24"};
+obsolete(snmp, oid_to_name, 1) ->
+ {deprecated, "use snmpa:oid_to_name/1 instead.", "OTP 24"};
+obsolete(snmp, oid_to_name, 2) ->
+ {deprecated, "use snmpa:oid_to_name/2 instead.", "OTP 24"};
+obsolete(snmp, register_subagent, 3) ->
+ {deprecated, "use snmpa:register_subagent/3 instead.", "OTP 24"};
+obsolete(snmp, send_notification, 3) ->
+ {deprecated, "use snmpa:send_notification/3 instead.", "OTP 24"};
+obsolete(snmp, send_notification, 4) ->
+ {deprecated, "use snmpa:send_notification/4 instead.", "OTP 24"};
+obsolete(snmp, send_notification, 5) ->
+ {deprecated, "use snmpa:send_notification/5 instead.", "OTP 24"};
+obsolete(snmp, send_notification, 6) ->
+ {deprecated, "use snmpa:send_notification/6 instead.", "OTP 24"};
+obsolete(snmp, send_trap, 3) ->
+ {deprecated, "use snmpa:send_trap/3 instead.", "OTP 24"};
+obsolete(snmp, send_trap, 4) ->
+ {deprecated, "use snmpa:send_trap/4 instead.", "OTP 24"};
+obsolete(snmp, unload_mibs, 2) ->
+ {deprecated, "use snmpa:unload_mibs/2 instead.", "OTP 24"};
+obsolete(snmp, unregister_subagent, 2) ->
+ {deprecated, "use snmpa:unregister_subagent/2 instead.", "OTP 24"};
+obsolete(snmpa, old_info_format, 1) ->
+ {deprecated, "use \"new\" format instead", "OTP 24"};
+obsolete(snmpm, async_get, 3) ->
+ {deprecated, "use snmpm:async_get2/3 instead.", "OTP 25"};
+obsolete(snmpm, async_get, 4) ->
+ {deprecated, "use snmpm:async_get2/4 instead.", "OTP 25"};
+obsolete(snmpm, async_get, 5) ->
+ {deprecated, "use snmpm:async_get2/4 instead.", "OTP 25"};
+obsolete(snmpm, async_get, 6) ->
+ {deprecated, "use snmpm:async_get2/4 instead.", "OTP 25"};
+obsolete(snmpm, async_get_bulk, 5) ->
+ {deprecated, "use snmpm:async_get_bulk2/5 instead.", "OTP 25"};
+obsolete(snmpm, async_get_bulk, 6) ->
+ {deprecated, "use snmpm:async_get_bulk2/6 instead.", "OTP 25"};
+obsolete(snmpm, async_get_bulk, 7) ->
+ {deprecated, "use snmpm:async_get_bulk2/6 instead.", "OTP 25"};
+obsolete(snmpm, async_get_bulk, 8) ->
+ {deprecated, "use snmpm:async_get_bulk2/6 instead.", "OTP 25"};
+obsolete(snmpm, async_get_next, 3) ->
+ {deprecated, "use snmpm:async_get_next2/3 instead.", "OTP 25"};
+obsolete(snmpm, async_get_next, 4) ->
+ {deprecated, "use snmpm:async_get_next2/4 instead.", "OTP 25"};
+obsolete(snmpm, async_get_next, 5) ->
+ {deprecated, "use snmpm:async_get_next2/4 instead.", "OTP 25"};
+obsolete(snmpm, async_get_next, 6) ->
+ {deprecated, "use snmpm:async_get_next2/4 instead.", "OTP 25"};
+obsolete(snmpm, async_set, 3) ->
+ {deprecated, "use snmpm:async_set2/3 instead.", "OTP 25"};
+obsolete(snmpm, async_set, 4) ->
+ {deprecated, "use snmpm:async_set2/4 instead.", "OTP 25"};
+obsolete(snmpm, async_set, 5) ->
+ {deprecated, "use snmpm:async_set2/4 instead.", "OTP 25"};
+obsolete(snmpm, async_set, 6) ->
+ {deprecated, "use snmpm:async_set2/4 instead.", "OTP 25"};
+obsolete(snmpm, sync_get, 3) ->
+ {deprecated, "use snmpm:sync_get2/3 instead.", "OTP 25"};
+obsolete(snmpm, sync_get, 4) ->
+ {deprecated, "use snmpm:sync_get2/4 instead.", "OTP 25"};
+obsolete(snmpm, sync_get, 5) ->
+ {deprecated, "use snmpm:sync_get2/4 instead.", "OTP 25"};
+obsolete(snmpm, sync_get, 6) ->
+ {deprecated, "use snmpm:sync_get2/4 instead.", "OTP 25"};
+obsolete(snmpm, sync_get_bulk, 5) ->
+ {deprecated, "use snmpm:sync_get_bulk2/5 instead.", "OTP 25"};
+obsolete(snmpm, sync_get_bulk, 6) ->
+ {deprecated, "use snmpm:sync_get_bulk2/6 instead.", "OTP 25"};
+obsolete(snmpm, sync_get_bulk, 7) ->
+ {deprecated, "use snmpm:sync_get_bulk2/6 instead.", "OTP 25"};
+obsolete(snmpm, sync_get_bulk, 8) ->
+ {deprecated, "use snmpm:sync_get_bulk2/6 instead.", "OTP 25"};
+obsolete(snmpm, sync_get_next, 3) ->
+ {deprecated, "use snmpm:sync_get_next2/3 instead.", "OTP 25"};
+obsolete(snmpm, sync_get_next, 4) ->
+ {deprecated, "use snmpm:sync_get_next2/4 instead.", "OTP 25"};
+obsolete(snmpm, sync_get_next, 5) ->
+ {deprecated, "use snmpm:sync_get_next2/4 instead.", "OTP 25"};
+obsolete(snmpm, sync_get_next, 6) ->
+ {deprecated, "use snmpm:sync_get_next2/4 instead.", "OTP 25"};
+obsolete(snmpm, sync_set, 3) ->
+ {deprecated, "use snmpm:sync_set2/3 instead.", "OTP 25"};
+obsolete(snmpm, sync_set, 4) ->
+ {deprecated, "use snmpm:sync_set2/4 instead.", "OTP 25"};
+obsolete(snmpm, sync_set, 5) ->
+ {deprecated, "use snmpm:sync_set2/4 instead.", "OTP 25"};
+obsolete(snmpm, sync_set, 6) ->
+ {deprecated, "use snmpm:sync_set2/4 instead.", "OTP 25"};
+obsolete(ssl, cipher_suites, 0) ->
+ {deprecated, "use cipher_suites/2,3 instead", "OTP 24"};
+obsolete(ssl, cipher_suites, 1) ->
+ {deprecated, "use cipher_suites/2,3 instead", "OTP 24"};
+obsolete(sys, get_debug, 3) ->
+ {deprecated, "incorrectly documented and only for internal use. Can often be replaced with sys:get_log/1"};
+obsolete(wxCalendarCtrl, enableYearChange, 1) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxCalendarCtrl, enableYearChange, 2) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxClientDC, new, 0) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxCursor, new, 3) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxCursor, new, 4) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxDC, computeScaleAndOrigin, 1) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxGraphicsRenderer, createLinearGradientBrush, 7) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxGraphicsRenderer, createRadialGradientBrush, 8) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxGridCellEditor, endEdit, 4) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxGridCellEditor, paintBackground, 3) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxIdleEvent, canSend, 1) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxMDIClientWindow, new, 1) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxMDIClientWindow, new, 2) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxPaintDC, new, 0) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxPostScriptDC, getResolution, 0) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxPostScriptDC, setResolution, 1) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(wxWindowDC, new, 0) ->
+ {deprecated, "not available in wxWidgets-2.9 and later"};
+obsolete(core_lib, get_anno, 1) ->
+ {removed, "use cerl:get_ann/1 instead"};
+obsolete(core_lib, is_literal, 1) ->
+ {removed, "use cerl:is_literal/1 instead"};
+obsolete(core_lib, is_literal_list, 1) ->
+ {removed, "use cerl:is_literal_list/1 instead"};
+obsolete(core_lib, literal_value, 1) ->
+ {removed, "use cerl:concrete/1 instead"};
+obsolete(core_lib, set_anno, 2) ->
+ {removed, "use cerl:set_ann/2 instead"};
+obsolete(crypto, aes_cbc_128_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, aes_cbc_128_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, aes_cbc_256_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, aes_cbc_256_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, aes_cbc_ivec, 2) ->
+ {removed, "use crypto:next_iv/2 instead"};
+obsolete(crypto, aes_cfb_128_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, aes_cfb_128_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, aes_ctr_decrypt, 3) ->
+ {removed, "use crypto:stream_decrypt/2 instead"};
+obsolete(crypto, aes_ctr_encrypt, 3) ->
+ {removed, "use crypto:stream_encrypt/2 instead"};
+obsolete(crypto, aes_ctr_stream_decrypt, 2) ->
+ {removed, "use crypto:stream_decrypt/2 instead"};
+obsolete(crypto, aes_ctr_stream_encrypt, 2) ->
+ {removed, "use crypto:stream_encrypt/2 instead"};
+obsolete(crypto, aes_ctr_stream_init, 2) ->
+ {removed, "use crypto:stream_init/3 instead"};
+obsolete(crypto, blowfish_cbc_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, blowfish_cbc_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, blowfish_cfb64_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, blowfish_cfb64_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, blowfish_ecb_decrypt, 2) ->
+ {removed, "use crypto:block_decrypt/3 instead"};
+obsolete(crypto, blowfish_ecb_encrypt, 2) ->
+ {removed, "use crypto:block_encrypt/3 instead"};
+obsolete(crypto, blowfish_ofb64_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, blowfish_ofb64_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des3_cbc_decrypt, 5) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des3_cbc_encrypt, 5) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des3_cfb_decrypt, 5) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des3_cfb_encrypt, 5) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des3_ede3_cbc_decrypt, 5) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des_cbc_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des_cbc_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des_cbc_ivec, 2) ->
+ {removed, "use crypto:next_iv/2 instead"};
+obsolete(crypto, des_cfb_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, des_cfb_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, des_cfb_ivec, 2) ->
+ {removed, "use crypto:next_iv/3 instead"};
+obsolete(crypto, des_ecb_decrypt, 2) ->
+ {removed, "use crypto:block_decrypt/3 instead"};
+obsolete(crypto, des_ecb_encrypt, 2) ->
+ {removed, "use crypto:block_encrypt/3 instead"};
+obsolete(crypto, des_ede3_cbc_encrypt, 5) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, dh_compute_key, 3) ->
+ {removed, "use crypto:compute_key/4 instead"};
+obsolete(crypto, dh_generate_key, 1) ->
+ {removed, "use crypto:generate_key/2 instead"};
+obsolete(crypto, dh_generate_key, 2) ->
+ {removed, "use crypto:generate_key/3 instead"};
+obsolete(crypto, erlint, 1) ->
+ {removed, "only needed by other removed functions"};
+obsolete(crypto, info, 0) ->
+ {removed, "use crypto:module_info/0 instead"};
+obsolete(crypto, md4, 1) ->
+ {removed, "use crypto:hash/2 instead"};
+obsolete(crypto, md4_final, 1) ->
+ {removed, "use crypto:hash_final/1 instead"};
+obsolete(crypto, md4_init, 0) ->
+ {removed, "use crypto:hash_init/1 instead"};
+obsolete(crypto, md4_update, 2) ->
+ {removed, "use crypto:hash_update/2 instead"};
+obsolete(crypto, md5, 1) ->
+ {removed, "use crypto:hash/2 instead"};
+obsolete(crypto, md5_final, 1) ->
+ {removed, "use crypto:hash_final/1 instead"};
+obsolete(crypto, md5_init, 0) ->
+ {removed, "use crypto:hash_init/1 instead"};
+obsolete(crypto, md5_mac, 2) ->
+ {removed, "use crypto:hmac/3 instead"};
+obsolete(crypto, md5_mac_96, 2) ->
+ {removed, "use crypto:hmac/4 instead"};
+obsolete(crypto, md5_update, 2) ->
+ {removed, "use crypto:hash_update/2 instead"};
+obsolete(crypto, mod_exp, 3) ->
+ {removed, "use crypto:mod_pow/3 instead"};
+obsolete(crypto, mpint, 1) ->
+ {removed, "only needed by other removed functions"};
+obsolete(crypto, rand_bytes, 1) ->
+ {removed, "use crypto:strong_rand_bytes/1 instead"};
+obsolete(crypto, rc2_40_cbc_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, rc2_40_cbc_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, rc2_cbc_decrypt, 3) ->
+ {removed, "use crypto:block_decrypt/4 instead"};
+obsolete(crypto, rc2_cbc_encrypt, 3) ->
+ {removed, "use crypto:block_encrypt/4 instead"};
+obsolete(crypto, rc4_encrypt, 2) ->
+ {removed, "use crypto:stream_encrypt/2 instead"};
+obsolete(crypto, rc4_encrypt_with_state, 2) ->
+ {removed, "use crypto:stream_encrypt/2 instead"};
+obsolete(crypto, rc4_set_key, 2) ->
+ {removed, "use crypto:stream_init/2 instead"};
+obsolete(crypto, sha, 1) ->
+ {removed, "use crypto:hash/2 instead"};
+obsolete(crypto, sha_final, 1) ->
+ {removed, "use crypto:hash_final/1 instead"};
+obsolete(crypto, sha_init, 0) ->
+ {removed, "use crypto:hash_init/1 instead"};
+obsolete(crypto, sha_mac, 2) ->
+ {removed, "use crypto:hmac/3 instead"};
+obsolete(crypto, sha_mac, 3) ->
+ {removed, "use crypto:hmac/4 instead"};
+obsolete(crypto, sha_mac_96, 2) ->
+ {removed, "use crypto:hmac/4 instead"};
+obsolete(crypto, sha_update, 2) ->
+ {removed, "use crypto:hash_update/2 instead"};
+obsolete(crypto, strong_rand_mpint, 3) ->
+ {removed, "only needed by other removed functions"};
+obsolete(erl_lint, modify_line, 2) ->
+ {removed, "use erl_parse:map_anno/2 instead"};
+obsolete(erl_parse, get_attribute, 2) ->
+ {removed, "erl_anno:{column,line,location,text}/1 instead"};
+obsolete(erl_parse, get_attributes, 1) ->
+ {removed, "erl_anno:{column,line,location,text}/1 instead"};
+obsolete(erl_parse, set_line, 2) ->
+ {removed, "use erl_anno:set_line/2"};
+obsolete(erl_scan, set_attribute, 3) ->
+ {removed, "use erl_anno:set_line/2 instead"};
+obsolete(erlang, hash, 2) ->
+ {removed, "use erlang:phash2/2 instead"};
+obsolete(httpd_conf, check_enum, 2) ->
+ {removed, "use lists:member/2 instead"};
+obsolete(httpd_conf, clean, 1) ->
+ {removed, "use sting:strip/1 instead or possibly the re module"};
+obsolete(httpd_conf, custom_clean, 3) ->
+ {removed, "use sting:strip/1 instead or possibly the re module"};
+obsolete(httpd_conf, is_directory, 1) ->
+ {removed, "use filelib:is_dir/1 instead"};
+obsolete(httpd_conf, is_file, 1) ->
+ {removed, "use filelib:is_file/1 instead"};
+obsolete(httpd_conf, make_integer, 1) ->
+ {removed, "use erlang:list_to_integer/1 instead"};
+obsolete(rpc, safe_multi_server_call, 2) ->
+ {removed, "use rpc:multi_server_call/2 instead"};
+obsolete(rpc, safe_multi_server_call, 3) ->
+ {removed, "use rpc:multi_server_call/3 instead"};
+obsolete(ssl, connection_info, 1) ->
+ {removed, "use ssl:connection_information/[1,2] instead"};
+obsolete(ssl, negotiated_next_protocol, 1) ->
+ {removed, "use ssl:negotiated_protocol/1 instead"};
+obsolete(auth, node_cookie, _) ->
+ {deprecated, "use erlang:set_cookie/2 and net_adm:ping/1 instead"};
+obsolete(crypto, next_iv, _) ->
+ {deprecated, "see the 'New and Old API' chapter of the CRYPTO User's guide", "OTP 24"};
+obsolete(crypto, stream_init, _) ->
+ {deprecated, "use crypto:crypto_init/3 + crypto:crypto_update/2 + crypto:crypto_final/1 or crypto:crypto_one_time/4 instead", "OTP 24"};
+obsolete(filename, find_src, _) ->
+ {deprecated, "use filelib:find_source/1,3 instead", "OTP 24"};
+obsolete(ssl, ssl_accept, _) ->
+ {deprecated, "use ssl_handshake/1,2,3 instead", "OTP 24"};
+obsolete(asn1ct, decode, _) ->
+ {removed, "use Mod:decode/2 instead"};
+obsolete(asn1ct, encode, _) ->
+ {removed, "use Mod:encode/2 instead"};
+obsolete(crypto, dss_sign, _) ->
+ {removed, "use crypto:sign/4 instead"};
+obsolete(crypto, dss_verify, _) ->
+ {removed, "use crypto:verify/5 instead"};
+obsolete(crypto, rsa_sign, _) ->
+ {removed, "use crypto:sign/4 instead"};
+obsolete(crypto, rsa_verify, _) ->
+ {removed, "use crypto:verify/5 instead"};
+obsolete(erl_scan, attributes_info, _) ->
+ {removed, "erl_anno:{column,line,location,text}/1 instead"};
+obsolete(erl_scan, token_info, _) ->
+ {removed, "erl_scan:{category,column,line,location,symbol,text}/1 instead"};
+obsolete(erl_tidy, _, _) ->
+ {deprecated, "use https://github.com/richcarl/erl_tidy", "OTP 24"};
+obsolete(gen_fsm, _, _) ->
+ {deprecated, "use the 'gen_statem' module instead"};
+obsolete(igor, _, _) ->
+ {deprecated, "use https://github.com/richcarl/igor", "OTP 24"};
+obsolete(pg2, _, _) ->
+ {deprecated, "use 'pg' instead", "OTP 24"};
+obsolete(random, _, _) ->
+ {deprecated, "use the 'rand' module instead"};
+obsolete(os_mon_mib, _, _) ->
+ {removed, "this module was removed in OTP 22.0"};
+obsolete(_,_,_) -> no.
-dialyzer({no_match, obsolete_type/3}).
-obsolete_type(Module, Name, NumberOfVariables) ->
- case obsolete_type_1(Module, Name, NumberOfVariables) of
- {deprecated=Tag,{_,_,_}=Replacement} ->
- {Tag,Replacement,"in a future release"};
- {_,String}=Ret when is_list(String) ->
- Ret;
- {_,_,_}=Ret ->
- Ret;
- no ->
- no
- end.
+obsolete_type(crypto, retired_cbc_cipher_aliases, 0) ->
+ {deprecated, "Use aes_*_cbc or des_ede3_cbc"};
+obsolete_type(crypto, retired_cfb_cipher_aliases, 0) ->
+ {deprecated, "Use aes_*_cfb8, aes_*_cfb128 or des_ede3_cfb"};
+obsolete_type(crypto, retired_ctr_cipher_aliases, 0) ->
+ {deprecated, "Use aes_*_ctr"};
+obsolete_type(crypto, retired_ecb_cipher_aliases, 0) ->
+ {deprecated, "Use aes_*_ecb"};
+obsolete_type(erl_scan, column, 0) ->
+ {removed, "use erl_anno:column() instead"};
+obsolete_type(erl_scan, line, 0) ->
+ {removed, "use erl_anno:line() instead"};
+obsolete_type(erl_scan, location, 0) ->
+ {removed, "use erl_anno:location() instead"};
+obsolete_type(_,_,_) -> no.
-obsolete_type_1(erl_scan,column,0) ->
- {removed,{erl_anno,column,0},"19.0"};
-obsolete_type_1(erl_scan,line,0) ->
- {removed,{erl_anno,line,0},"19.0"};
-obsolete_type_1(erl_scan,location,0) ->
- {removed,{erl_anno,location,0},"19.0"};
-obsolete_type_1(_,_,_) ->
- no.
diff --git a/lib/stdlib/src/otp_internal.hrl b/lib/stdlib/src/otp_internal.hrl
new file mode 100644
index 0000000000..17e15da68f
--- /dev/null
+++ b/lib/stdlib/src/otp_internal.hrl
@@ -0,0 +1,36 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% This file is included by the file "otp_internal.erl", which is
+%% auto-generated by stdlib/scripts/update_deprecations
+%%
+
+-export([obsolete/3, obsolete_type/3]).
+
+-type tag() :: 'deprecated' | 'removed'. %% | 'experimental'.
+-type mfas() :: mfa() | {atom(), atom(), [byte()]} | string().
+-type release() :: string().
+
+-spec obsolete(module(), atom(), arity()) ->
+ 'no' | {tag(), string()} | {tag(), mfas(), release()}.
+
+-spec obsolete_type(module(), atom(), arity()) ->
+ 'no' | {tag(), string()} | {tag(), mfas(), release()}.
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index cfbaf8b242..58e6faf950 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
spawn/3, spawn_link/3, spawn/4, spawn_link/4,
spawn_opt/2, spawn_opt/3, spawn_opt/4, spawn_opt/5,
start/3, start/4, start/5, start_link/3, start_link/4, start_link/5,
+ start_monitor/3, start_monitor/4, start_monitor/5,
hibernate/3,
init_ack/1, init_ack/2,
init_p/3,init_p/5,format/1,format/2,format/3,report_cb/2,
@@ -39,25 +40,21 @@
-export([wake_up/3]).
-export_type([spawn_option/0]).
+-export_type([start_spawn_option/0]).
-include("logger.hrl").
%%-----------------------------------------------------------------------------
--type priority_level() :: 'high' | 'low' | 'max' | 'normal'.
--type max_heap_size() :: non_neg_integer() |
- #{ size => non_neg_integer(),
- kill => true,
- error_logger => true}.
--type spawn_option() :: 'link'
- | 'monitor'
- | {'priority', priority_level()}
- | {'max_heap_size', max_heap_size()}
- | {'min_heap_size', non_neg_integer()}
- | {'min_bin_vheap_size', non_neg_integer()}
- | {'fullsweep_after', non_neg_integer()}
- | {'message_queue_data',
- 'off_heap' | 'on_heap' | 'mixed' }.
+-type start_spawn_option() :: 'link'
+ | {'priority', erlang:priority_level()}
+ | {'max_heap_size', erlang:max_heap_size()}
+ | {'min_heap_size', non_neg_integer()}
+ | {'min_bin_vheap_size', non_neg_integer()}
+ | {'fullsweep_after', non_neg_integer()}
+ | {'message_queue_data', erlang:message_queue_data() }.
+
+-type spawn_option() :: erlang:spawn_opt_option().
-type dict_or_pid() :: pid()
| (ProcInfo :: [_])
@@ -65,6 +62,14 @@
%%-----------------------------------------------------------------------------
+-define(VERIFY_NO_MONITOR_OPT(M, F, A, T, Opts),
+ case lists:member(monitor, Opts) of
+ true -> erlang:error(badarg, [M,F,A,T,Opts]);
+ false -> ok
+ end).
+
+%%-----------------------------------------------------------------------------
+
-spec spawn(Fun) -> pid() when
Fun :: function().
@@ -141,17 +146,16 @@ spawn_link(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
Ancestors = get_ancestors(),
erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]).
--spec spawn_opt(Fun, SpawnOpts) -> pid() when
+-spec spawn_opt(Fun, SpawnOpts) -> pid() | {pid(), reference()} 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, SpawnOpts) -> pid() when
+-spec spawn_opt(Node, Function, SpawnOpts) -> pid() | {pid(), reference()} when
Node :: node(),
Function :: function(),
SpawnOpts :: [spawn_option()].
@@ -159,10 +163,9 @@ spawn_opt(F, Opts) when is_function(F) ->
spawn_opt(Node, F, Opts) when is_function(F) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,F], Opts).
--spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() when
+-spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() | {pid(), reference()} when
Module :: module(),
Function :: atom(),
Args :: [term()],
@@ -171,10 +174,9 @@ spawn_opt(Node, F, Opts) when is_function(F) ->
spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts).
--spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() when
+-spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() | {pid(), reference()} when
Node :: node(),
Module :: module(),
Function :: atom(),
@@ -184,30 +186,13 @@ spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
spawn_opt(Node, M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts).
-%% OTP-6345
-%% monitor spawn_opt option is currently not possible to use
-check_for_monitor(SpawnOpts) ->
- case lists:member(monitor, SpawnOpts) of
- true ->
- erlang:error(badarg);
- false ->
- false
- end.
-
spawn_mon(M,F,A) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
erlang:spawn_monitor(?MODULE, init_p, [Parent,Ancestors,M,F,A]).
-spawn_opt_mon(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
- Parent = get_my_name(),
- Ancestors = get_ancestors(),
- check_for_monitor(Opts),
- erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], [monitor|Opts]).
-
-spec hibernate(Module, Function, Args) -> no_return() when
Module :: module(),
Function :: atom(),
@@ -216,14 +201,6 @@ spawn_opt_mon(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
erlang:hibernate(?MODULE, wake_up, [M, F, A]).
-ensure_link(SpawnOpts) ->
- case lists:member(link, SpawnOpts) of
- true ->
- SpawnOpts;
- false ->
- [link|SpawnOpts]
- end.
-
-spec init_p(pid(), [pid()], function()) -> term().
init_p(Parent, Ancestors, Fun) when is_function(Fun) ->
@@ -299,20 +276,32 @@ start(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
Ret :: term() | {error, Reason :: term()}.
start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
- PidRef = spawn_mon(M, F, A),
- sync_wait_mon(PidRef, Timeout).
+ sync_start(spawn_mon(M, F, A), Timeout).
-spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when
Module :: module(),
Function :: atom(),
Args :: [term()],
Time :: timeout(),
- SpawnOpts :: [spawn_option()],
+ SpawnOpts :: [start_spawn_option()],
Ret :: term() | {error, Reason :: term()}.
start(M, F, A, Timeout, SpawnOpts) when is_atom(M), is_atom(F), is_list(A) ->
- PidRef = spawn_opt_mon(M, F, A, SpawnOpts),
- sync_wait_mon(PidRef, Timeout).
+ ?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
+ sync_start(?MODULE:spawn_opt(M, F, A, [monitor|SpawnOpts]), Timeout).
+
+sync_start({Pid, Ref}, Timeout) ->
+ receive
+ {ack, Pid, Return} ->
+ erlang:demonitor(Ref, [flush]),
+ Return;
+ {'DOWN', Ref, process, Pid, Reason} ->
+ {error, Reason}
+ after Timeout ->
+ erlang:demonitor(Ref, [flush]),
+ kill_flush(Pid),
+ {error, timeout}
+ end.
-spec start_link(Module, Function, Args) -> Ret when
Module :: module(),
@@ -331,60 +320,88 @@ start_link(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
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).
+ sync_start_link(?MODULE:spawn_link(M, F, A), Timeout).
-spec start_link(Module, Function, Args, Time, SpawnOpts) -> Ret when
Module :: module(),
Function :: atom(),
Args :: [term()],
Time :: timeout(),
- SpawnOpts :: [spawn_option()],
+ SpawnOpts :: [start_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)),
- sync_wait(Pid, Timeout).
+ ?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
+ sync_start_link(?MODULE:spawn_opt(M, F, A, [link|SpawnOpts]), Timeout).
-sync_wait(Pid, Timeout) ->
+sync_start_link(Pid, Timeout) ->
receive
{ack, Pid, Return} ->
- Return;
+ Return;
{'EXIT', Pid, Reason} ->
- {error, Reason}
+ {error, Reason}
after Timeout ->
- unlink(Pid),
- exit(Pid, kill),
- flush(Pid),
- {error, timeout}
+ kill_flush(Pid),
+ {error, timeout}
end.
-sync_wait_mon({Pid, Ref}, Timeout) ->
+-spec start_monitor(Module, Function, Args) -> {Ret, Mon} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Mon :: reference(),
+ Ret :: term() | {error, Reason :: term()}.
+
+start_monitor(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
+ start_monitor(M, F, A, infinity).
+
+-spec start_monitor(Module, Function, Args, Time) -> {Ret, Mon} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Time :: timeout(),
+ Mon :: reference(),
+ Ret :: term() | {error, Reason :: term()}.
+
+start_monitor(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
+ sync_start_monitor(spawn_mon(M, F, A), Timeout).
+
+-spec start_monitor(Module, Function, Args, Time, SpawnOpts) -> {Ret, Mon} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Time :: timeout(),
+ SpawnOpts :: [start_spawn_option()],
+ Mon :: reference(),
+ Ret :: term() | {error, Reason :: term()}.
+
+start_monitor(M,F,A,Timeout,SpawnOpts) when is_atom(M),
+ is_atom(F),
+ is_list(A) ->
+ ?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
+ sync_start_monitor(?MODULE:spawn_opt(M, F, A, [monitor|SpawnOpts]),
+ Timeout).
+
+sync_start_monitor({Pid, Ref}, Timeout) ->
receive
{ack, Pid, Return} ->
- erlang:demonitor(Ref, [flush]),
- Return;
- {'DOWN', Ref, _Type, Pid, Reason} ->
- {error, Reason};
- {'EXIT', Pid, Reason} -> %% link as spawn_opt?
- erlang:demonitor(Ref, [flush]),
- {error, Reason}
+ {Return, Ref};
+ {'DOWN', Ref, process, Pid, Reason} = Down ->
+ self() ! Down,
+ {{error, Reason}, Ref}
after Timeout ->
- erlang:demonitor(Ref, [flush]),
- exit(Pid, kill),
- flush(Pid),
- {error, timeout}
+ kill_flush(Pid),
+ {{error, timeout}, Ref}
end.
--spec flush(pid()) -> 'true'.
+-spec kill_flush(Pid) -> 'ok' when
+ Pid :: pid().
-flush(Pid) ->
- receive
- {'EXIT', Pid, _} ->
- true
- after 0 ->
- true
- end.
+kill_flush(Pid) ->
+ unlink(Pid),
+ exit(Pid, kill),
+ receive {'EXIT', Pid, _} -> ok after 0 -> ok end,
+ ok.
-spec init_ack(Parent, Ret) -> 'ok' when
Parent :: pid(),
@@ -784,20 +801,114 @@ format(CrashReport, Encoding, Depth) ->
encoding => Encoding,
single_line => false}).
-do_format([OwnReport,LinkReport], #{single_line:=Single}=Extra) ->
+do_format([OwnReport,LinkReport], Extra) ->
+ #{encoding:=Enc, single_line:=Single, chars_limit:=Limit0} = Extra,
Indent = if Single -> "";
true -> " "
end,
- MyIndent = Indent ++ Indent,
- Sep = nl(Single,"; "),
- OwnFormat = format_report(OwnReport, MyIndent, Extra),
- LinkFormat = lists:join(Sep,format_link_report(LinkReport, MyIndent, Extra)),
Nl = nl(Single," "),
- Str = io_lib:format("~scrasher:"++Nl++"~ts"++Sep++"~sneighbours:"++Nl++"~ts",
- [Indent,OwnFormat,Indent,LinkFormat]),
- lists:flatten(Str).
+ Sep = nl(Single, report_separator()),
+ {PartLimit, Limit} =
+ case Limit0 of
+ unlimited ->
+ {Limit0, Limit0};
+ _ when is_integer(Limit0) ->
+ %% HardcodedSize is the length of the hardcoded heading +
+ %% separators in the final format string below,
+ %% including neighbours. Just make sure the limit
+ %% does not become negative.
+ Num = length(OwnReport),
+ HardcodedSize = (length(Indent) + length("crasher")
+ + length(Nl) + length(Sep)
+ + (length(Sep) * Num)),
+ Limit1 = max(Limit0-HardcodedSize, 1),
+
+ %% Divide the available characters over all report
+ %% parts. Spend one third of the characters on the
+ %% crash reason, and let the rest of the elements
+ %% (including the neighbours) share the other two
+ %% thirds. This is to make sure we see a good part of
+ %% the crash reason. Most of the other elements in the
+ %% crasher's report are quite small, so we don't loose
+ %% a lot of info from these anyway.
+ EL = Limit1 div 3,
+ PL = (Limit1-EL) div (Num),
+ {PL, Limit1}
+ end,
+ LinkFormat = format_link_reports(LinkReport, Indent, Extra, PartLimit),
+ LinkFormatSize = size(Enc, LinkFormat),
+
+ OwnFormat = format_own_report(OwnReport, Indent, Extra,
+ LinkFormatSize, PartLimit, Limit),
+ io_lib:format("~scrasher:"++Nl++"~ts"++Sep++"~ts",
+ [Indent,OwnFormat,LinkFormat]).
+
+format_own_report(OwnReport, Indent, Extra, LinkFormatSize, PartLimit, Limit0) ->
+ MyIndent = Indent ++ Indent,
+ case separate_error_info(OwnReport) of
+ {First,{Class,Reason,StackTrace},Rest} ->
+ F = format_report(First, MyIndent, Extra, PartLimit),
+ R = format_report(Rest, MyIndent, Extra, PartLimit),
+ #{encoding:=Enc, single_line:=Single} = Extra,
+ Sep = nl(Single, part_separator()),
+ Limit = case Limit0 of
+ unlimited ->
+ Limit0;
+ _ when is_integer(Limit0) ->
+ %% Some of the report parts are quite small,
+ %% and we can use the leftover chars to show
+ %% more of the error_info part.
+ SizeOfOther = (size(Enc, F)
+ +size(Enc, R)
+ -length(Sep)*(length(F)+length(R))
+ +LinkFormatSize),
+ max(Limit0-SizeOfOther, 1)
+ end,
+ EI = format_exception(Class, Reason, StackTrace, Extra, Limit),
+ lists:join(Sep, [F, EI, R]);
+ no ->
+ Limit = case Limit0 of
+ unlimited ->
+ Limit0;
+ _ when is_integer(Limit0) ->
+ max(Limit0-LinkFormatSize, 1)
+ end,
+ format_report(OwnReport, MyIndent, Extra, Limit)
+ end.
-format_link_report([Link|Reps], Indent0, #{single_line:=Single}=Extra) ->
+separate_error_info(Report) ->
+ try
+ lists:splitwith(fun(A) -> element(1, A) =/= error_info end, Report)
+ of
+ {First, [{error_info,ErrorInfo}|Rest]} ->
+ {First,ErrorInfo,Rest};
+ _ -> no
+ catch _:_ -> no
+ end.
+
+%% If the size of the total report is limited by chars_limit, then
+%% print only the pids.
+format_link_reports(LinkReports, Indent, Extra, PartLimit)
+ when is_integer(PartLimit) ->
+ #{encoding:=Enc, depth:=Depth, single_line:=Single} = Extra,
+ Pids = [P || {neighbour,[{pid,P}|_]} <- LinkReports],
+ {P,Tl} = p(Enc,Depth),
+ Width = if Single -> "0";
+ true -> ""
+ end,
+ io_lib:format(Indent++"neighbours: ~"++Width++P,
+ [Pids|Tl],
+ [{chars_limit,PartLimit}]);
+format_link_reports(LinkReports, Indent, Extra, PartLimit) ->
+ #{single_line:=Single} = Extra,
+ MyIndent = Indent ++ Indent,
+ LinkFormat =
+ lists:join(nl(Single, report_separator()),
+ format_link_report(LinkReports, MyIndent, Extra, PartLimit)),
+ [Indent,"neighbours:",nl(Single," "),LinkFormat].
+
+format_link_report([Link|Reps], Indent0, Extra, PartLimit) ->
+ #{single_line:=Single} = Extra,
Rep = case Link of
{neighbour,Rep0} -> Rep0;
_ -> Link
@@ -806,63 +917,70 @@ format_link_report([Link|Reps], Indent0, #{single_line:=Single}=Extra) ->
true -> Indent0
end,
LinkIndent = [" ",Indent],
- [[Indent,"neighbour:",nl(Single," "),format_report(Rep, LinkIndent, Extra)]|
- format_link_report(Reps, Indent, Extra)];
-format_link_report(Rep, Indent, Extra) ->
- format_report(Rep, Indent, Extra).
-
-format_report(Rep, Indent, #{single_line:=Single}=Extra) when is_list(Rep) ->
- lists:join(nl(Single,", "),format_rep(Rep, Indent, Extra));
-format_report(Rep, Indent0, #{encoding:=Enc,depth:=Depth,
- chars_limit:=Limit,single_line:=Single}) ->
+ [[Indent,"neighbour:",nl(Single," "),
+ format_report(Rep, LinkIndent, Extra, PartLimit)]|
+ format_link_report(Reps, Indent, Extra, PartLimit)];
+format_link_report(Rep, Indent, Extra, PartLimit) ->
+ format_report(Rep, Indent, Extra, PartLimit).
+
+format_report(Rep, Indent, Extra, Limit) when is_list(Rep) ->
+ #{single_line:=Single} = Extra,
+ lists:join(nl(Single, part_separator()),
+ format_rep(Rep, Indent, Extra, Limit));
+format_report(Rep, Indent0, Extra, Limit) ->
+ #{encoding:=Enc, depth:=Depth, single_line:=Single} = Extra,
{P,Tl} = p(Enc,Depth),
{Indent,Width} = if Single -> {"","0"};
true -> {Indent0,""}
end,
- Opts = if is_integer(Limit) -> [{chars_limit,Limit}];
- true -> []
- end,
+ Opts = chars_limit_opt(Limit),
io_lib:format("~s~"++Width++P, [Indent, Rep | Tl], Opts).
-format_rep([{initial_call,InitialCall}|Rep], Indent, Extra) ->
- [format_mfa(Indent, InitialCall, Extra)|format_rep(Rep, Indent, Extra)];
-format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], Indent, Extra) ->
- [format_exception(Class, Reason, StackTrace, Extra)|
- format_rep(Rep, Indent, Extra)];
-format_rep([{Tag,Data}|Rep], Indent, Extra) ->
- [format_tag(Indent, Tag, Data, Extra)|format_rep(Rep, Indent, Extra)];
-format_rep(_, _, _Extra) ->
+format_rep([{initial_call,InitialCall}|Rep], Indent, Extra, Limit) ->
+ [format_mfa(Indent, InitialCall, Extra, Limit)|
+ format_rep(Rep, Indent, Extra, Limit)];
+format_rep([{Tag,Data}|Rep], Indent, Extra, Limit) ->
+ [format_tag(Indent, Tag, Data, Extra, Limit)|
+ format_rep(Rep, Indent, Extra, Limit)];
+format_rep(_, _, _Extra, _Limit) ->
[].
-format_exception(Class, Reason, StackTrace,
- #{encoding:=Enc,depth:=Depth,chars_limit:=Limit,
- single_line:=Single}=Extra) ->
- PF = pp_fun(Extra),
+format_exception(Class, Reason, StackTrace, Extra, Limit) ->
+ #{encoding:=Enc,depth:=Depth, single_line:=Single} = Extra,
StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
if Single ->
{P,Tl} = p(Enc,Depth),
- Opts = if is_integer(Limit) -> [{chars_limit,Limit}];
- true -> []
- end,
+ Opts = chars_limit_opt(Limit),
[atom_to_list(Class), ": ",
io_lib:format("~0"++P,[{Reason,StackTrace}|Tl],Opts)];
true ->
+ %% Notice that each call to PF uses chars_limit, which
+ %% means that the total size of the formatted exception
+ %% can exceed the limit a lot.
+ PF = pp_fun(Extra, Enc),
EI = " ",
- [EI, erl_error:format_exception(1+length(EI), Class, Reason,
- StackTrace, StackFun, PF, Enc)]
+ Lim = case Limit of
+ unlimited -> -1;
+ _ -> Limit
+ end,
+ FE = erl_error:format_exception(1+length(EI), Class, Reason,
+ StackTrace, StackFun, PF, Enc,
+ Lim),
+ [EI, FE]
end.
-format_mfa(Indent0, {M,F,Args}=StartF, #{encoding:=Enc,single_line:=Single}=Extra) ->
+format_mfa(Indent0, {M,F,Args}=StartF, Extra, Limit) ->
+ #{encoding:=Enc,single_line:=Single} = Extra,
Indent = if Single -> "";
true -> Indent0
end,
try
A = length(Args),
- [Indent,"initial call: ",atom_to_list(M),$:,to_string(F, Enc),$/,
+ [Indent,"initial call: ",to_string(M, Enc),$:,to_string(F, Enc),$/,
integer_to_list(A)]
catch
error:_ ->
- format_tag(Indent, initial_call, StartF, Extra)
+ format_tag(Indent, initial_call, StartF, Extra, Limit)
end.
to_string(A, latin1) ->
@@ -870,27 +988,25 @@ to_string(A, latin1) ->
to_string(A, _) ->
io_lib:write_atom(A).
-pp_fun(#{encoding:=Enc,depth:=Depth,chars_limit:=Limit,single_line:=Single}) ->
+pp_fun(Extra, Enc) ->
+ #{encoding:=Enc,depth:=Depth, single_line:=Single} = Extra,
{P,Tl} = p(Enc, Depth),
Width = if Single -> "0";
true -> ""
end,
- Opts = if is_integer(Limit) -> [{chars_limit,Limit}];
- true -> []
- end,
- fun(Term, I) ->
- io_lib:format("~" ++ Width ++ "." ++ integer_to_list(I) ++ P,
- [Term|Tl], Opts)
+ fun(Term, I, Limit) ->
+ S = io_lib:format("~" ++ Width ++ "." ++ integer_to_list(I) ++ P,
+ [Term|Tl], [{chars_limit, Limit}]),
+ {S, sub(Limit, S, Enc)}
end.
-format_tag(Indent0, Tag, Data, #{encoding:=Enc,depth:=Depth,chars_limit:=Limit,single_line:=Single}) ->
+format_tag(Indent0, Tag, Data, Extra, Limit) ->
+ #{encoding:=Enc,depth:=Depth,single_line:=Single} = Extra,
{P,Tl} = p(Enc, Depth),
{Indent,Width} = if Single -> {"","0"};
true -> {Indent0,""}
end,
- Opts = if is_integer(Limit) -> [{chars_limit,Limit}];
- true -> []
- end,
+ Opts = chars_limit_opt(Limit),
io_lib:format("~s~" ++ Width ++ "p: ~" ++ Width ++ ".18" ++ P,
[Indent, Tag, Data|Tl], Opts).
@@ -902,12 +1018,35 @@ p(Encoding, Depth) ->
P = modifier(Encoding) ++ Letter,
{P, Tl}.
+report_separator() -> "; ".
+
+part_separator() -> ", ".
+
+chars_limit_opt(CharsLimit) ->
+ [{chars_limit, CharsLimit} || is_integer(CharsLimit)].
+
modifier(latin1) -> "";
modifier(_) -> "t".
nl(true,Else) -> Else;
nl(false,_) -> "\n".
+%% Make sure T does change sign.
+sub(T, _, _Enc) when T < 0 -> T;
+sub(T, E, Enc) ->
+ Sz = size(Enc, E),
+ if
+ T >= Sz ->
+ T - Sz;
+ true ->
+ 0
+ end.
+
+size(latin1, S) ->
+ iolist_size(S);
+size(_, S) ->
+ string:length(S).
+
%%% -----------------------------------------------------------
%%% Stop a process and wait for it to terminate
%%% -----------------------------------------------------------
diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl
index 3ce68887ae..9216c3bdb3 100644
--- a/lib/stdlib/src/proplists.erl
+++ b/lib/stdlib/src/proplists.erl
@@ -220,7 +220,7 @@ get_value(Key, [P | Ps], Default) ->
{_, Value} ->
Value;
_ ->
- %% Don</code>t continue the search!
+ %% Don't continue the search!
Default
end;
true ->
@@ -419,7 +419,7 @@ substitute_aliases_1([], P) ->
%% <p>Example: <code>substitute_negations([{no_foo, foo}], L)</code>
%% will replace any atom <code>no_foo</code> or tuple <code>{no_foo,
%% true}</code> in <code>L</code> with <code>{foo, false}</code>, and
-%% any other tuple <code>{no_foo, ...}</code> with <code>foo</code.</p>
+%% any other tuple <code>{no_foo, ...}</code> with <code>foo</code>.</p>
%%
%% @see get_bool/2
%% @see substitute_aliases/2
@@ -639,24 +639,24 @@ normalize(L, []) ->
Rest :: [term()].
split(List, Keys) ->
- {Store, Rest} = split(List, dict:from_list([{K, []} || K <- Keys]), []),
- {[lists:reverse(dict:fetch(K, Store)) || K <- Keys],
+ {Store, Rest} = split(List, maps:from_list([{K, []} || K <- Keys]), []),
+ {[lists:reverse(map_get(K, Store)) || K <- Keys],
lists:reverse(Rest)}.
split([P | Ps], Store, Rest) ->
if is_atom(P) ->
- case dict:is_key(P, Store) of
+ case is_map_key(P, Store) of
true ->
- split(Ps, dict_prepend(P, P, Store), Rest);
+ split(Ps, maps_prepend(P, P, Store), Rest);
false ->
split(Ps, Store, [P | Rest])
end;
tuple_size(P) >= 1 ->
%% Note that Key does not have to be an atom in this case.
Key = element(1, P),
- case dict:is_key(Key, Store) of
+ case is_map_key(Key, Store) of
true ->
- split(Ps, dict_prepend(Key, P, Store), Rest);
+ split(Ps, maps_prepend(Key, P, Store), Rest);
false ->
split(Ps, Store, [P | Rest])
end;
@@ -666,5 +666,5 @@ split([P | Ps], Store, Rest) ->
split([], Store, Rest) ->
{Store, Rest}.
-dict_prepend(Key, Val, Dict) ->
- dict:store(Key, [Val | dict:fetch(Key, Dict)], Dict).
+maps_prepend(Key, Val, Dict) ->
+ Dict#{Key := [Val | map_get(Key, Dict)]}.
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index a1c1117e31..713ed1f896 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -785,7 +785,7 @@ merge_binding_structs(Bs1, Bs2) ->
aux_name1(Name, N, AllNames) ->
SN = name_suffix(Name, N),
- case sets:is_element(SN, AllNames) of
+ case gb_sets:is_member(SN, AllNames) of
true -> aux_name1(Name, N + 1, AllNames);
false -> {SN, N}
end.
@@ -1357,7 +1357,7 @@ flatten_abstr(E, VN, _Vars, Body) ->
{VN, Body, E}.
abstract_vars(Abstract) ->
- sets:from_list(ordsets:to_list(vars(Abstract))).
+ gb_sets:from_list(ordsets:to_list(vars(Abstract))).
collect([]=L) ->
L;
diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl
index 4a39f8ae9d..7cf631d85d 100644
--- a/lib/stdlib/src/qlc_pt.erl
+++ b/lib/stdlib/src/qlc_pt.erl
@@ -511,7 +511,7 @@ used_genvar_check(FormsNoShadows, State) ->
Acc0 = {State#state.intro_vars, [{atom, anno0(), true}]},
{_, {[], Exprs}} = qual_fold(F, Acc0, [], FormsNoShadows, State),
FunctionNames = [Name || {function, _, Name, _, _} <- FormsNoShadows],
- UniqueFName = qlc:aux_name(used_genvar, 1, sets:from_list(FunctionNames)),
+ UniqueFName = qlc:aux_name(used_genvar, 1, gb_sets:from_list(FunctionNames)),
A = anno0(),
{function,A,UniqueFName,0,[{clause,A,[],[],lists:reverse(Exprs)}]}.
@@ -613,8 +613,8 @@ q_intro_vars(QId, [{QId, IVs} | QsIVs], IVsSoFar) -> {QsIVs, IVs ++ IVsSoFar}.
transform(FormsNoShadows, State) ->
_ = erlang:system_flag(backtrace_depth, 500),
IntroVars = State#state.intro_vars,
- AllVars = sets:from_list(ordsets:to_list(qlc:vars(FormsNoShadows))),
- ?DEBUG("AllVars = ~p~n", [sets:to_list(AllVars)]),
+ AllVars = gb_sets:from_list(ordsets:to_list(qlc:vars(FormsNoShadows))),
+ ?DEBUG("AllVars = ~p~n", [gb_sets:to_list(AllVars)]),
F1 = fun(QId, {generate,_,P,LE}, Foo, {GoI,SI}) ->
{{QId,GoI,SI,{gen,P,LE}},Foo,{GoI + 3, SI + 2}};
(QId, F, Foo, {GoI,SI}) ->
@@ -632,10 +632,10 @@ transform(FormsNoShadows, State) ->
{_,Source0} = qual_fold(fun(_QId, {generate,_,_P,_E}=Q, Dict, Foo) ->
{Q,Dict,Foo};
(QId, F, Dict, Foo) ->
- {F,dict:store(QId, F, Dict),Foo}
- end, dict:new(), [], FormsNoShadows, State),
+ {F,maps:put(QId, F, Dict),Foo}
+ end, maps:new(), [], FormsNoShadows, State),
{_,Source} = qlc_mapfold(fun(Id, {lc,_L,E,_Qs}=LC, Dict) ->
- {LC,dict:store(Id, E, Dict)}
+ {LC,maps:put(Id, E, Dict)}
end, Source0, FormsNoShadows, State),
@@ -685,7 +685,7 @@ transform(FormsNoShadows, State) ->
FunW = {'fun',L,{clauses,[{clause,L,AsW,[],
[{match,L,{var,L,Fun},FunC},
{call,L,{var,L,Fun},As0}]}]}},
- {ok, OrigE0} = dict:find(Id, Source),
+ OrigE0 = map_get(Id, Source),
OrigE = undo_no_shadows(OrigE0, State),
QCode = qcode(OrigE, XQCs, Source, L, State),
Qdata = qdata(XQCs, L),
@@ -2361,7 +2361,7 @@ qcode(E, QCs, Source, L, State) ->
qcode([{_QId, {_QIvs, {{gen,P,_LE,_GV}, GoI, _SI}}} | QCs], Source, State) ->
[{GoI,undo_no_shadows(P, State)} | qcode(QCs, Source, State)];
qcode([{QId, {_QIVs, {{fil,_F}, GoI, _SI}}} | QCs], Source, State) ->
- {ok,OrigF} = dict:find(QId, Source),
+ OrigF = map_get(QId, Source),
[{GoI,undo_no_shadows(OrigF, State)} | qcode(QCs, Source, State)];
qcode([], _Source, _State) ->
[].
@@ -2666,12 +2666,12 @@ no_shadows(Forms0, State) ->
%%
%% The original names of variables are kept in a table in State.
%% undo_no_shadows/2 re-creates the original code.
- AllVars = sets:from_list(ordsets:to_list(qlc:vars(Forms0))),
- ?DEBUG("nos AllVars = ~p~n", [sets:to_list(AllVars)]),
+ AllVars = gb_sets:from_list(ordsets:to_list(qlc:vars(Forms0))),
+ ?DEBUG("nos AllVars = ~p~n", [gb_sets:to_list(AllVars)]),
VFun = fun(_Id, LC, Vs) -> nos(LC, Vs) end,
LI = ets:new(?APIMOD,[]),
UV = ets:new(?APIMOD,[]),
- D0 = dict:new(),
+ D0 = maps:new(),
S1 = {LI, D0, UV, AllVars, [], State},
_ = qlc_mapfold(VFun, S1, Forms0, State),
?DEBUG("UsedIntroVars = ~p~n", [ets:match_object(UV, '_')]),
@@ -2781,7 +2781,7 @@ nos_var(Anno, Name, State) ->
end.
used_var(V, Vs, UV) ->
- case dict:find(V, Vs) of
+ case maps:find(V, Vs) of
{ok,Value} ->
VN = qlc:name_suffix(V, Value),
_ = ets:update_counter(UV, VN, 1),
@@ -2796,10 +2796,10 @@ next_var(V, Vs, AllVars, LI, UV) ->
end,
true = ets:insert(LI, {V, NValue}),
VN = qlc:name_suffix(V, NValue),
- case sets:is_element(VN, AllVars) of
+ case gb_sets:is_member(VN, AllVars) of
true -> next_var(V, Vs, AllVars, LI, UV);
false -> true = ets:insert(UV, {VN, 0}),
- NVs = dict:store(V, NValue, Vs),
+ NVs = maps:put(V, NValue, Vs),
{VN, NVs}
end.
diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl
index 11c0aa8d2b..9fe3782f92 100644
--- a/lib/stdlib/src/queue.erl
+++ b/lib/stdlib/src/queue.erl
@@ -37,7 +37,7 @@
%% Mis-spelled, deprecated.
-export([lait/1]).
--deprecated([lait/1]).
+-deprecated([{lait,1,"use queue:liat/1 instead"}]).
%%--------------------------------------------------------------------------
%% Efficient implementation of double ended fifo queues
diff --git a/lib/stdlib/src/random.erl b/lib/stdlib/src/random.erl
index 46dabb4323..8d6a35f031 100644
--- a/lib/stdlib/src/random.erl
+++ b/lib/stdlib/src/random.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
%%
-module(random).
--deprecated(module).
+-deprecated({'_','_',"use the 'rand' module instead"}).
%% Reasonable random number generator.
%% The method is attributed to B. A. Wichmann and I. D. Hill
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 041a89f909..b397b2fc36 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -1186,6 +1186,8 @@ record_bindings(Recs0, Bs0) ->
read_records(FileOrModule, Opts0) ->
Opts = lists:delete(report_warnings, Opts0),
case find_file(FileOrModule) of
+ {beam, Beam, File} ->
+ read_records_from_beam(Beam, File);
{files,[File]} ->
read_file_records(File, Opts);
{files,Files} ->
@@ -1204,10 +1206,22 @@ read_records(FileOrModule, Opts0) ->
find_file(Mod) when is_atom(Mod) ->
case code:which(Mod) of
File when is_list(File) ->
- {files,[File]};
- preloaded ->
- {_M,_Bin,File} = code:get_object_code(Mod),
- {files,[File]};
+ %% Special cases:
+ %% - Modules not in the code path (loaded with code:load_abs/1):
+ %% code:get_object_code/1 only searches in the code path
+ %% but code:which/1 finds all loaded modules
+ %% - File can also be a file in an archive,
+ %% beam_lib:chunks/2 cannot handle such paths but
+ %% erl_prim_loader:get_file/1 can
+ case erl_prim_loader:get_file(File) of
+ {ok, Beam, _} ->
+ {beam, Beam, File};
+ error ->
+ {error, nofile}
+ end;
+ preloaded ->
+ {_M, Beam, File} = code:get_object_code(Mod),
+ {beam, Beam, File};
_Else -> % non_existing, interpreted, cover_compiled
{error,nofile}
end;
@@ -1222,28 +1236,31 @@ find_file(File) ->
read_file_records(File, Opts) ->
case filename:extension(File) of
".beam" ->
- case beam_lib:chunks(File, [abstract_code,"CInf"]) of
- {ok,{_Mod,[{abstract_code,{Version,Forms}},{"CInf",CB}]}} ->
- case record_attrs(Forms) of
- [] when Version =:= raw_abstract_v1 ->
- [];
- [] ->
- %% If the version is raw_X, then this test
- %% is unnecessary.
- try_source(File, CB);
- Records ->
- Records
- end;
- {ok,{_Mod,[{abstract_code,no_abstract_code},{"CInf",CB}]}} ->
- try_source(File, CB);
- Error ->
- %% Could be that the "Abst" chunk is missing (pre R6).
- Error
- end;
+ read_records_from_beam(File, File);
_ ->
parse_file(File, Opts)
end.
+read_records_from_beam(Beam, File) ->
+ case beam_lib:chunks(Beam, [abstract_code,"CInf"]) of
+ {ok,{_Mod,[{abstract_code,{Version,Forms}},{"CInf",CB}]}} ->
+ case record_attrs(Forms) of
+ [] when Version =:= raw_abstract_v1 ->
+ [];
+ [] ->
+ %% If the version is raw_X, then this test
+ %% is unnecessary.
+ try_source(File, CB);
+ Records ->
+ Records
+ end;
+ {ok,{_Mod,[{abstract_code,no_abstract_code},{"CInf",CB}]}} ->
+ try_source(File, CB);
+ Error ->
+ %% Could be that the "Abst" chunk is missing (pre R6).
+ Error
+ end.
+
%% This is how the debugger searches for source files. See int.erl.
try_source(Beam, RawCB) ->
EbinDir = filename:dirname(Beam),
diff --git a/lib/stdlib/src/shell_default.erl b/lib/stdlib/src/shell_default.erl
index a0c1d98513..4819beb62b 100644
--- a/lib/stdlib/src/shell_default.erl
+++ b/lib/stdlib/src/shell_default.erl
@@ -28,6 +28,7 @@
erlangrc/1,bi/1, regs/0, flush/0,pwd/0,ls/0,ls/1,cd/1,
y/1, y/2,
xm/1, bt/1, q/0,
+ h/1, h/2, h/3, ht/1, ht/2, ht/3, hcb/1, hcb/2, hcb/3,
ni/0, nregs/0]).
-export([ih/0,iv/0,im/0,ii/1,ii/2,iq/1,ini/1,ini/2,inq/1,ib/2,ib/3,
@@ -43,7 +44,16 @@ help() ->
format("e(N) -- repeat the expression in query <N>\n"),
format("f() -- forget all variable bindings\n"),
format("f(X) -- forget the binding of variable X\n"),
- format("h() -- history\n"),
+ format("h() -- history\n"),
+ format("h(Mod) -- help about module\n"),
+ format("h(Mod,Func)-- help about function in module\n"),
+ format("h(Mod,Func,Arity) -- help about function with arity in module\n"),
+ format("ht(Mod) -- help about a module's types\n"),
+ format("ht(Mod,Type) -- help about type in module\n"),
+ format("ht(Mod,Type,Arity) -- help about type with arity in module\n"),
+ format("hcb(Mod) -- help about a module's callbacks\n"),
+ format("hcb(Mod,CB) -- help about callback in module\n"),
+ format("hcb(Mod,CB,Arity) -- help about callback with arity in module\n"),
format("history(N) -- set how many previous commands to keep\n"),
format("results(N) -- set how many previous command results to keep\n"),
format("catch_exception(B) -- how exceptions are handled\n"),
@@ -76,6 +86,15 @@ c(File, Opt, Filter) -> c:c(File, Opt, Filter).
cd(D) -> c:cd(D).
erlangrc(X) -> c:erlangrc(X).
flush() -> c:flush().
+h(M) -> c:h(M).
+h(M,F) -> c:h(M,F).
+h(M,F,A) -> c:h(M,F,A).
+ht(M) -> c:ht(M).
+ht(M,F) -> c:ht(M,F).
+ht(M,F,A) -> c:ht(M,F,A).
+hcb(M) -> c:hcb(M).
+hcb(M,F) -> c:hcb(M,F).
+hcb(M,F,A) -> c:hcb(M,F,A).
i() -> c:i().
i(X,Y,Z) -> c:i(X,Y,Z).
l(Mod) -> c:l(Mod).
diff --git a/lib/stdlib/src/shell_docs.erl b/lib/stdlib/src/shell_docs.erl
new file mode 100644
index 0000000000..5abb3a7f0c
--- /dev/null
+++ b/lib/stdlib/src/shell_docs.erl
@@ -0,0 +1,1018 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(shell_docs).
+
+-include_lib("kernel/include/eep48.hrl").
+
+-export([render/2, render/3, render/4, render/5]).
+-export([render_type/2, render_type/3, render_type/4, render_type/5]).
+-export([render_callback/2, render_callback/3, render_callback/4, render_callback/5]).
+
+%% Used by chunks.escript in erl_docgen
+-export([validate/1, normalize/1]).
+
+%% Convinience functions
+-export([get_doc/1, get_doc/3, get_type_doc/3, get_callback_doc/3]).
+
+-record(config, { docs,
+ encoding,
+ ansi,
+ io_opts = io:getopts(),
+ columns
+ }).
+
+-define(ALL_ELEMENTS,[a,p,'div',br,h1,h2,h3,i,em,pre,code,ul,ol,li,dl,dt,dd]).
+%% inline elements are:
+-define(INLINE,[i,em,code,a]).
+-define(IS_INLINE(ELEM),(((ELEM) =:= a) orelse ((ELEM) =:= code)
+ orelse ((ELEM) =:= i) orelse ((ELEM) =:= em))).
+%% non-inline elements are:
+-define(BLOCK,[p,'div',pre,br,ul,ol,li,dl,dt,dd,h1,h2,h3]).
+-define(IS_BLOCK(ELEM),not ?IS_INLINE(ELEM)).
+-define(IS_PRE(ELEM),(((ELEM) =:= pre))).
+
+%% If you update the below types, make sure to update the documentation in
+%% erl_docgen/doc/src/doc_storage.xml as well!!!
+-type docs_v1() :: #docs_v1{}.
+-type config() :: #{ encoding => unicode | latin1,
+ columns => pos_integer(),
+ ansi => boolean() }.
+-type chunk_elements() :: [chunk_element()].
+-type chunk_element() :: {chunk_element_type(),chunk_element_attrs(),
+ chunk_elements()} | binary().
+-type chunk_element_attrs() :: [chunk_element_attr()].
+-type chunk_element_attr() :: {atom(),unicode:chardata()}.
+-type chunk_element_type() :: chunk_element_inline_type() | chunk_element_block_type().
+-type chunk_element_inline_type() :: a | code | em | i.
+-type chunk_element_block_type() :: p | 'div' | br | pre | ul |
+ ol | li | dl | dt | dd | h1 | h2 | h3.
+
+-spec validate(Module) -> ok when
+ Module :: module() | docs_v1().
+%% Simple validation of erlang doc chunk. Check that all tags are supported and
+%% that the signature is correct.
+validate(Module) when is_atom(Module) ->
+ {ok, Doc} = code:get_doc(Module),
+ validate(Doc);
+validate(#docs_v1{ module_doc = MDocs, docs = AllDocs }) ->
+
+ %% Check some macro in-variants
+ AE = lists:sort(?ALL_ELEMENTS),
+ AE = lists:sort(?INLINE ++ ?BLOCK),
+ true = lists:all(fun(Elem) -> ?IS_INLINE(Elem) end, ?INLINE),
+ true = lists:all(fun(Elem) -> ?IS_BLOCK(Elem) end, ?BLOCK),
+
+ _ = validate_docs(MDocs),
+ lists:foreach(fun({_,_Anno, Sig, Docs, _Meta}) ->
+ case lists:all(fun erlang:is_binary/1, Sig) of
+ false -> throw({invalid_signature,Sig});
+ true -> ok
+ end,
+ validate_docs(Docs)
+ end, AllDocs),
+ ok.
+
+validate_docs(hidden) ->
+ ok;
+validate_docs(none) ->
+ ok;
+validate_docs(#{} = MDocs) ->
+ _ = maps:map(fun(_Key,MDoc) -> validate_docs(MDoc,[]) end, MDocs),
+ ok.
+validate_docs([H|T],Path) when is_tuple(H) ->
+ _ = validate_docs(H,Path),
+ validate_docs(T,Path);
+validate_docs({br,Attr,Content} = Br,Path) ->
+ if Attr =:= [], Content =:= [] ->
+ ok;
+ true ->
+ throw({content_to_allowed_in_br,Br,Path})
+ end;
+validate_docs({Tag,Attr,Content},Path) ->
+
+ %% Test that we only have li's within ul and ol
+ case (Tag =/= li) andalso (length(Path) > 0) andalso ((hd(Path) =:= ul) orelse (hd(Path) =:= ol)) of
+ true ->
+ throw({only_li_allowed_within_ul_or_ol,Tag,Path});
+ _ ->
+ ok
+ end,
+
+ %% Test that we only have dd's and dt's within dl
+ case (Tag =/= dd) andalso (Tag =/= dt) andalso (length(Path) > 0) andalso (hd(Path) =:= dl) of
+ true ->
+ throw({only_dd_or_dt_allowed_within_dl,Tag,Path});
+ _ ->
+ ok
+ end,
+
+ %% Test that we do not have p's within p's
+ case Tag =:= p andalso lists:member(p, Path) of
+ true ->
+ throw({nested_p_not_allowed,Tag,Path});
+ false ->
+ ok
+ end,
+ %% Test that there are no block tags within a pre, h1, h2 or h3
+ case lists:member(pre,Path) or lists:member(h1,Path) or
+ lists:member(h2,Path) or lists:member(h3,Path) of
+ true when ?IS_BLOCK(Tag) ->
+ throw({cannot_put_block_tag_within_pre,Tag,Path});
+ _ ->
+ ok
+ end,
+ %% Test that a block tag is not within an inline tag
+ case lists:member(Tag,?BLOCK) of
+ true ->
+ case lists:any(fun(P) -> ?IS_INLINE(P) end, Path) of
+ true ->
+ throw({cannot_put_inline_tag_outside_block, Tag, Path});
+ false ->
+ ok
+ end;
+ false ->
+ ok
+ end,
+ case lists:member(Tag,?ALL_ELEMENTS) of
+ false ->
+ throw({invalid_tag,Tag,Path});
+ true ->
+ ok
+ end,
+ case lists:all(fun({Key,Val}) -> is_atom(Key) andalso is_binary(Val) end,Attr) of
+ true -> ok;
+ false -> throw({invalid_attribute,{Tag,Attr}})
+ end,
+ validate_docs(Content,[Tag | Path]);
+validate_docs([Chars | T], Path) when is_binary(Chars) ->
+ validate_docs(T, Path);
+validate_docs([],_) ->
+ ok.
+
+%% Follows algorithm described here:
+%% * https://medium.com/@patrickbrosset/when-does-white-space-matter-in-html-b90e8a7cdd33
+%% which in turn follows this:
+%% * https://www.w3.org/TR/css-text-3/#white-space-processing
+-spec normalize(Docs) -> NormalizedDocs when
+ Docs :: chunk_elements(),
+ NormalizedDocs :: chunk_elements().
+normalize(Docs) ->
+ Trimmed = normalize_trim(Docs,true),
+ normalize_space(Trimmed).
+
+normalize_trim(Bin,true) when is_binary(Bin) ->
+ %% Remove any whitespace (except \n) before or after a newline
+ NoSpace = re:replace(Bin,"[^\\S\n]*\n+[^\\S\n]*","\n",[unicode,global]),
+ %% Replace any tabs with space
+ NoTab = re:replace(NoSpace,"\t"," ",[unicode,global]),
+ %% Replace any newlines with space
+ NoNewLine = re:replace(NoTab,"\\v"," ",[unicode,global]),
+ %% Replace any sequences of \s with a single " "
+ re:replace(NoNewLine,"\\s+"," ",[unicode,global,{return,binary}]);
+normalize_trim(Bin,false) when is_binary(Bin) ->
+ Bin;
+normalize_trim([{pre,Attr,Content}|T],Trim) ->
+ [{pre,Attr,normalize_trim(Content,false)} | normalize_trim(T,Trim)];
+normalize_trim([{Tag,Attr,Content}|T],Trim) ->
+ [{Tag,Attr,normalize_trim(Content,Trim)} | normalize_trim(T,Trim)];
+normalize_trim([<<>>|T],Trim) ->
+ normalize_trim(T,Trim);
+normalize_trim([B1,B2|T],Trim) when is_binary(B1),is_binary(B2) ->
+ normalize_trim([<<B1/binary,B2/binary>> | T],Trim);
+normalize_trim([H|T],Trim) ->
+ [normalize_trim(H,Trim) | normalize_trim(T,Trim)];
+normalize_trim([],_Trim) ->
+ [].
+
+%% We want to remove any duplicate spaces, even if they
+%% cross into other inline elements.
+%% For non-inline elements we just need to make sure that any
+%% leading or trailing spaces are stripped.
+normalize_space([{Pre,Attr,Content}|T]) when ?IS_PRE(Pre) ->
+ [{Pre,Attr,trim_first_and_last(Content,$\n)} | normalize_space(T)];
+normalize_space([{Block,Attr,Content}|T]) when ?IS_BLOCK(Block) ->
+ [{Block,Attr,normalize_space(Content)} | normalize_space(T)];
+normalize_space([]) ->
+ [];
+normalize_space(Elems) ->
+ {InlineElems, T} =
+ lists:splitwith(fun(E) ->
+ is_binary(E) orelse (is_tuple(E) andalso ?IS_INLINE(element(1,E)))
+ end, Elems),
+ trim_inline(InlineElems) ++ normalize_space(T).
+
+trim_inline(Content) ->
+ {NewContent,_} = trim_inline(Content,false),
+ trim_first_and_last(NewContent,$ ).
+trim_inline([Bin|T],false) when is_binary(Bin) ->
+ LastElem = binary:at(Bin,byte_size(Bin)-1),
+ case trim_inline(T,LastElem =:= $ ) of
+ {[B2 | NewT],NewState} when is_binary(B2) ->
+ {[<<Bin/binary,B2/binary>>|NewT],NewState};
+ {NewT, NewState} ->
+ {[Bin|NewT],NewState}
+ end;
+trim_inline([<<" ">>|T],true) ->
+ trim_inline(T,true);
+trim_inline([<<" ",Bin/binary>>|T],true) when is_binary(Bin) ->
+ trim_inline([Bin | T],true);
+trim_inline([Bin|T],true) when is_binary(Bin) ->
+ trim_inline([Bin|T],false);
+trim_inline([{Elem,Attr,Content}|T],TrimSpace) ->
+ {NewContent,ContentTrimSpace} = trim_inline(Content,TrimSpace),
+ {NewT,TTrimSpace} = trim_inline(T,ContentTrimSpace),
+ IsAnchor = (Elem =:= a) andalso proplists:is_defined(id,Attr),
+ if NewContent == [] andalso (not IsAnchor) ->
+ %% Remove if all content has been trimmed and this is not an anchor
+ {NewT, TTrimSpace};
+ true ->
+ {[{Elem,Attr,NewContent} | NewT], TTrimSpace}
+ end;
+trim_inline([],TrimSpace) ->
+ {[],TrimSpace}.
+
+
+%% This function removes the first and last What from the content.
+%% This is complicated by the fact that the first or last element
+%% may not have any binary, or have the binary deeply nested within.
+trim_first_and_last(Content, What) when What < 256 ->
+ {FirstTrimmed, _} = trim_first(Content,What),
+ {LastTrimmed, _} = trim_last(FirstTrimmed,What),
+ LastTrimmed.
+
+trim_first([Bin|T],What) when is_binary(Bin) ->
+ case Bin of
+ <<What>> ->
+ {T,true};
+ <<What,NewBin/binary>> ->
+ {[NewBin|T],true};
+ Bin ->
+ {[Bin|T],true}
+ end;
+trim_first([{Elem,Attr,Content} = Tag|T],What) ->
+ case trim_first(Content,What) of
+ {[],true} ->
+ {T,true};
+ {NewContent,true} ->
+ {[{Elem,Attr,NewContent}|T],true};
+ {Content,false} ->
+ {NewT,NewState} = trim_first(T,What),
+ {[Tag | NewT],NewState}
+ end;
+trim_first([],_What) ->
+ {[],false}.
+
+trim_last([Bin | T],What) when is_binary(Bin) ->
+ case trim_last(T,What) of
+ {NewT,true} ->
+ {[Bin | NewT],true};
+ {T,false} ->
+ PreSz = byte_size(Bin)-1,
+ case Bin of
+ <<What>> -> {T,true};
+ <<NewBin:PreSz/binary,What>> ->
+ {[NewBin|T],true};
+ Bin ->
+ {[Bin|T],true}
+ end
+ end;
+trim_last([{Elem,Attr,Content} = Tag|T],What) ->
+ case trim_last(T,What) of
+ {NewT,true} ->
+ {[Tag | NewT],true};
+ {T,false} ->
+ case trim_last(Content,What) of
+ {[],true} ->
+ %% If the content became empty and we processed some text
+ %% we remove the element.
+ {[],true};
+ {NewContent,NewState} ->
+ {[{Elem,Attr,NewContent}|T],NewState}
+ end
+ end;
+trim_last([],_What) ->
+ {[],false}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% API function for dealing with the function documentation
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec get_doc(Module :: module()) -> chunk_elements().
+get_doc(Module) ->
+ {ok, #docs_v1{ module_doc = ModuleDoc } } = code:get_doc(Module),
+ get_local_doc(Module, ModuleDoc).
+
+-spec get_doc(Module :: module(), Function, Arity) ->
+ [{{Function,Arity}, Anno, Signature, chunk_elements(), Metadata}] when
+ Function :: atom(),
+ Arity :: arity(),
+ Anno :: erl_anno:anno(),
+ Signature :: [binary()],
+ Metadata :: #{}.
+get_doc(Module, Function, Arity) ->
+ {ok, #docs_v1{ docs = Docs } } = code:get_doc(Module),
+ FnFunctions =
+ lists:filter(fun({{function, F, A},_Anno,_Sig,_Doc,_Meta}) ->
+ F =:= Function andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs),
+
+ [{F,A,S,get_local_doc({F,A},D),M} || {F,A,S,D,M} <- FnFunctions].
+
+-spec render(Module, Docs) -> unicode:chardata() when
+ Module :: module(),
+ Docs :: docs_v1().
+render(Module, #docs_v1{ } = D) when is_atom(Module) ->
+ render(Module, D, #{}).
+
+-spec render(Module, Docs, Config) -> unicode:chardata() when
+ Module :: module(),
+ Docs :: docs_v1(),
+ Config :: config();
+
+ (Module, Function, Docs) -> Res when
+ Module :: module(),
+ Function :: atom(),
+ Docs :: docs_v1(),
+ Res :: unicode:chardata() | {error,function_missing}.
+render(Module, #docs_v1{ module_doc = ModuleDoc } = D, Config)
+ when is_atom(Module), is_map(Config) ->
+ render_headers_and_docs([[{h2,[],[<<"\t",(atom_to_binary(Module))/binary>>]}]],
+ get_local_doc(Module, ModuleDoc), D, Config);
+render(_Module, Function, #docs_v1{ } = D) ->
+ render(_Module, Function, D, #{}).
+
+-spec render(Module, Function, Docs, Config) -> Res when
+ Module :: module(),
+ Function :: atom(),
+ Docs :: docs_v1(),
+ Config :: config(),
+ Res :: unicode:chardata() | {error,function_missing};
+
+ (Module, Function, Arity, Docs) -> Res when
+ Module :: module(),
+ Function :: atom(),
+ Arity :: arity(),
+ Docs :: docs_v1(),
+ Res :: unicode:chardata() | {error,function_missing}.
+render(Module, Function, #docs_v1{ docs = Docs } = D, Config)
+ when is_atom(Module), is_atom(Function), is_map(Config) ->
+ render_function(
+ lists:filter(fun({{function, F, _},_Anno,_Sig,_Doc,_Meta}) ->
+ F =:= Function;
+ (_) ->
+ false
+ end, Docs), D, Config);
+render(_Module, Function, Arity, #docs_v1{ } = D) ->
+ render(_Module, Function, Arity, D, #{}).
+
+-spec render(Module, Function, Arity, Docs, Config) -> Res when
+ Module :: module(),
+ Function :: atom(),
+ Arity :: arity(),
+ Docs :: docs_v1(),
+ Config :: config(),
+ Res :: unicode:chardata() | {error,function_missing}.
+render(Module, Function, Arity, #docs_v1{ docs = Docs } = D, Config)
+ when is_atom(Module), is_atom(Function), is_integer(Arity), is_map(Config) ->
+ render_function(
+ lists:filter(fun({{function, F, A},_Anno,_Sig,_Doc,_Meta}) ->
+ F =:= Function andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs), D, Config).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% API function for dealing with the type documentation
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec get_type_doc(Module :: module(), Type :: atom(), Arity :: arity()) ->
+ [{{Type,Arity}, Anno, Signature, chunk_elements(), Metadata}] when
+ Type :: atom(),
+ Arity :: arity(),
+ Anno :: erl_anno:anno(),
+ Signature :: [binary()],
+ Metadata :: #{}.
+get_type_doc(Module, Type, Arity) ->
+ {ok, #docs_v1{ docs = Docs } } = code:get_doc(Module),
+ FnFunctions =
+ lists:filter(fun({{type, T, A},_Anno,_Sig,_Doc,_Meta}) ->
+ T =:= Type andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs),
+ [{F,A,S,get_local_doc(F, D),M} || {F,A,S,D,M} <- FnFunctions].
+
+-spec render_type(Module, Docs) -> unicode:chardata() when
+ Module :: module(),
+ Docs :: docs_v1().
+render_type(Module, D) ->
+ render_type(Module, D, #{}).
+
+-spec render_type(Module, Docs, Config) -> unicode:chardata() when
+ Module :: module(),
+ Docs :: docs_v1(),
+ Config :: config();
+ (Module, Type, Docs) -> Res when
+ Module :: module(), Type :: atom(),
+ Docs :: docs_v1(),
+ Res :: unicode:chardata() | {error, type_missing}.
+render_type(Module, D = #docs_v1{}, Config) ->
+ render_signature_listing(Module, type, D, Config);
+render_type(Module, Type, D = #docs_v1{}) ->
+ render_type(Module, Type, D, #{}).
+
+-spec render_type(Module, Type, Docs, Config) -> Res when
+ Module :: module(), Type :: atom(),
+ Docs :: docs_v1(),
+ Config :: config(),
+ Res :: unicode:chardata() | {error, type_missing};
+ (Module, Type, Arity, Docs) -> Res when
+ Module :: module(), Type :: atom(), Arity :: arity(),
+ Docs :: docs_v1(),
+ Res :: unicode:chardata() | {error, type_missing}.
+render_type(_Module, Type, #docs_v1{ docs = Docs } = D, Config) ->
+ render_typecb_docs(
+ lists:filter(fun({{type, T, _},_Anno,_Sig,_Doc,_Meta}) ->
+ T =:= Type;
+ (_) ->
+ false
+ end, Docs), D, Config);
+render_type(_Module, Type, Arity, #docs_v1{ } = D) ->
+ render_type(_Module, Type, Arity, D, #{}).
+
+-spec render_type(Module, Type, Arity, Docs, Config) -> Res when
+ Module :: module(), Type :: atom(), Arity :: arity(),
+ Docs :: docs_v1(),
+ Config :: config(),
+ Res :: unicode:chardata() | {error, type_missing}.
+render_type(_Module, Type, Arity, #docs_v1{ docs = Docs } = D, Config) ->
+ render_typecb_docs(
+ lists:filter(fun({{type, T, A},_Anno,_Sig,_Doc,_Meta}) ->
+ T =:= Type andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs), D, Config).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% API function for dealing with the callback documentation
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec get_callback_doc(Module :: module(), Callback :: atom(), Arity :: arity()) ->
+ [{{Callback,Arity}, Anno, Signature, chunk_elements(), Metadata}] when
+ Callback :: atom(),
+ Arity :: arity(),
+ Anno :: erl_anno:anno(),
+ Signature :: [binary()],
+ Metadata :: #{}.
+get_callback_doc(Module, Callback, Arity) ->
+ {ok, #docs_v1{ docs = Docs } } = code:get_doc(Module),
+ FnFunctions =
+ lists:filter(fun({{callback, T, A},_Anno,_Sig,_Doc,_Meta}) ->
+ T =:= Callback andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs),
+ [{F,A,S,get_local_doc(F, D),M} || {F,A,S,D,M} <- FnFunctions].
+
+-spec render_callback(Module, Docs) -> unicode:chardata() when
+ Module :: module(),
+ Docs :: docs_v1().
+render_callback(Module, D) ->
+ render_callback(Module, D, #{}).
+
+-spec render_callback(Module, Docs, Config) -> unicode:chardata() when
+ Module :: module(),
+ Docs :: docs_v1(),
+ Config :: config();
+ (Module, Callback, Docs) -> Res when
+ Module :: module(), Callback :: atom(),
+ Docs :: docs_v1(),
+ Res :: unicode:chardata() | {error, callback_missing}.
+render_callback(_Module, Callback, #docs_v1{ } = D) ->
+ render_callback(_Module, Callback, D, #{});
+render_callback(Module, D, Config) ->
+ render_signature_listing(Module, callback, D, Config).
+
+-spec render_callback(Module, Callback, Docs, Config) -> Res when
+ Module :: module(), Callback :: atom(),
+ Docs :: docs_v1(),
+ Config :: config(),
+ Res :: unicode:chardata() | {error, callback_missing};
+ (Module, Callback, Arity, Docs) -> Res when
+ Module :: module(), Callback :: atom(), Arity :: arity(),
+ Docs :: docs_v1(),
+ Res :: unicode:chardata() | {error, callback_missing}.
+render_callback(_Module, Callback, Arity, #docs_v1{ } = D) ->
+ render_callback(_Module, Callback, Arity, D, #{});
+render_callback(_Module, Callback, #docs_v1{ docs = Docs } = D, Config) ->
+ render_typecb_docs(
+ lists:filter(fun({{callback, T, _},_Anno,_Sig,_Doc,_Meta}) ->
+ T =:= Callback;
+ (_) ->
+ false
+ end, Docs), D, Config).
+
+-spec render_callback(Module, Callback, Arity, Docs, Config) -> Res when
+ Module :: module(), Callback :: atom(), Arity :: arity(),
+ Docs :: docs_v1(),
+ Config :: config(),
+ Res :: unicode:chardata() | {error, callback_missing}.
+render_callback(_Module, Callback, Arity, #docs_v1{ docs = Docs } = D, Config) ->
+ render_typecb_docs(
+ lists:filter(fun({{callback, T, A},_Anno,_Sig,_Doc,_Meta}) ->
+ T =:= Callback andalso A =:= Arity;
+ (_) ->
+ false
+ end, Docs), D, Config).
+
+%% Get the docs in the correct locale if it exists.
+get_local_doc(MissingMod, Docs) when is_atom(MissingMod) ->
+ get_local_doc(atom_to_binary(MissingMod), Docs);
+get_local_doc({F,A}, Docs) ->
+ get_local_doc(unicode:characters_to_binary(io_lib:format("~tp/~p",[F,A])), Docs);
+get_local_doc(_Missing, #{ <<"en">> := Docs }) ->
+ %% English if it exists
+ normalize(Docs);
+get_local_doc(_Missing, ModuleDoc) when map_size(ModuleDoc) > 0 ->
+ %% Otherwise take first alternative found
+ normalize(maps:get(hd(maps:keys(ModuleDoc)), ModuleDoc));
+get_local_doc(Missing, hidden) ->
+ [{p,[],[<<"The documentation for ">>,Missing,
+ <<" is hidden. This probably means that it is internal "
+ "and not to be used by other applications.">>]}];
+get_local_doc(Missing, None) when None =:= none; None =:= #{} ->
+ [{p,[],[<<"There is no documentation for ">>,Missing]}].
+
+%%% Functions for rendering reference documentation
+render_function([], _D, _Config) ->
+ {error,function_missing};
+render_function(FDocs, #docs_v1{ docs = Docs } = D, Config) ->
+ Grouping =
+ lists:foldl(
+ fun({_Group,_Anno,_Sig,_Doc,#{ equiv := Group }} = Func,Acc) ->
+ Members = maps:get(Group, Acc, []),
+ Acc#{ Group => [Func|Members] };
+ ({Group, _Anno, _Sig, _Doc, _Meta} = Func, Acc) ->
+ Members = maps:get(Group, Acc, []),
+ Acc#{ Group => [Func|Members] }
+ end, #{}, lists:sort(FDocs)),
+ lists:map(
+ fun({{_,F,A} = Group,Members}) ->
+ Signatures = lists:flatmap(fun render_signature/1,lists:reverse(Members)),
+ case lists:search(fun({_,_,_,Doc,_}) ->
+ Doc =/= #{}
+ end, Members) of
+ {value, {_,_,_,Doc,_Meta}} ->
+ render_headers_and_docs(Signatures, get_local_doc({F,A},Doc), D, Config);
+ false ->
+ case lists:keyfind(Group, 1, Docs) of
+ false ->
+ render_headers_and_docs(Signatures, get_local_doc({F,A},none), D, Config);
+ {_,_,_,Doc,_} ->
+ render_headers_and_docs(Signatures, get_local_doc({F,A},Doc), D, Config)
+ end
+ end
+ end, maps:to_list(Grouping)).
+
+%% Render the signature of either function, type, or anything else really.
+render_signature({{_Type,_F,_A},_Anno,_Sigs,_Docs,#{ signature := Specs } = Meta}) ->
+ lists:flatmap(
+ fun(ASTSpec) ->
+ PPSpec = erl_pp:attribute(ASTSpec,[{encoding,utf8}]),
+ Spec =
+ case ASTSpec of
+ {_Attribute, _Line, opaque, _} ->
+ %% We do not want show the internals of the opaque type
+ hd(string:split(PPSpec,"::"));
+ _ ->
+ PPSpec
+ end,
+ BinSpec =
+ unicode:characters_to_binary(
+ string:trim(Spec, trailing, "\n")),
+ [{pre,[],[{em,[],BinSpec}]}|render_meta(Meta)]
+ end, Specs);
+render_signature({{_Type,_F,_A},_Anno,Sigs,_Docs,Meta}) ->
+ lists:flatmap(
+ fun(Sig) ->
+ [{h2,[],[<<"  "/utf8,Sig/binary>>]}|render_meta(Meta)]
+ end, Sigs).
+
+render_meta(M) ->
+ case render_meta_(M) of
+ [] -> [];
+ Meta ->
+ [[{dl,[],Meta}]]
+ end.
+render_meta_(#{ since := Vsn } = M) ->
+ [{dt,[],<<"Since">>},{dd,[],[Vsn]}
+ | render_meta_(maps:remove(since, M))];
+render_meta_(#{ deprecated := Depr } = M) ->
+ [{dt,[],<<"Deprecated">>},{dd,[],[Depr]}
+ | render_meta_(maps:remove(deprecated, M))];
+render_meta_(_) ->
+ [].
+
+render_headers_and_docs(Headers, DocContents, D, Config) ->
+ render_headers_and_docs(Headers, DocContents, init_config(D, Config)).
+render_headers_and_docs(Headers, DocContents, #config{} = Config) ->
+ ["\n",render_docs(
+ lists:flatmap(
+ fun(Header) ->
+ [{br,[],[]},Header]
+ end,Headers), Config),
+ "\n",
+ render_docs(DocContents, 2, Config)].
+
+%%% Functions for rendering type/callback documentation
+render_signature_listing(Module, Type, #docs_v1{ docs = Docs } = D, Config) ->
+ Slogan = [{h2,[],[<<"\t",(atom_to_binary(Module))/binary>>]},{br,[],[]}],
+ case lists:filter(fun({{T, _, _},_Anno,_Sig,_Doc,_Meta}) ->
+ Type =:= T
+ end, Docs) of
+ [] ->
+ render_docs(
+ Slogan ++ [<<"There are no ",(atom_to_binary(Type))/binary,"s "
+ "in this module">>], D, Config);
+ Headers ->
+ Hdr = lists:flatmap(
+ fun(Header) ->
+ [{br,[],[]},render_signature(Header)]
+ end,Headers),
+ render_docs(
+ Slogan ++
+ [{p,[],[<<"These ",(atom_to_binary(Type))/binary,"s "
+ "are documented in this module:">>]},
+ {br,[],[]}, Hdr], D, Config)
+ end.
+
+render_typecb_docs([], _D) ->
+ {error,type_missing};
+render_typecb_docs(TypeCBs, #config{} = D) when is_list(TypeCBs) ->
+ [render_typecb_docs(TypeCB, D) || TypeCB <- TypeCBs];
+render_typecb_docs({{_,F,A},_,_Sig,Docs,_Meta} = TypeCB, #config{} = D) ->
+ render_headers_and_docs(render_signature(TypeCB), get_local_doc({F,A},Docs), D).
+render_typecb_docs(Docs, D, Config) ->
+ render_typecb_docs(Docs, init_config(D, Config)).
+
+%%% General rendering functions
+render_docs(DocContents, #config{} = Config) ->
+ render_docs(DocContents, 0, Config).
+render_docs(DocContents, D, Config) when is_map(Config) ->
+ render_docs(DocContents, 0, init_config(D, Config));
+render_docs(DocContents, Ind, D = #config{}) when is_integer(Ind) ->
+ init_ansi(D),
+ try
+ {Doc,_} = trimnl(render_docs(DocContents, [], 0, Ind, D)),
+ Doc
+ after
+ clean_ansi()
+ end.
+
+init_config(D, Config) ->
+ DefaultOpts = io:getopts(),
+ DefaultEncoding = proplists:get_value(encoding, DefaultOpts, latin1),
+ Columns =
+ case maps:find(columns, Config) of
+ error ->
+ case io:columns() of
+ {ok, C} ->
+ C;
+ _ ->
+ 80
+ end;
+ {ok, C} ->
+ C
+ end,
+ #config{ docs = D,
+ encoding = maps:get(encoding, Config, DefaultEncoding),
+ ansi = maps:get(ansi, Config, undefined),
+ columns = Columns
+ }.
+
+render_docs(Elems,State,Pos,Ind,D) when is_list(Elems) ->
+ lists:mapfoldl(fun(Elem,P) ->
+%%% io:format("Elem: ~p (~p) (~p,~p)~n",[Elem,State,P,Ind]),
+ render_docs(Elem,State,P,Ind,D)
+ end,Pos,Elems);
+render_docs(Elem,State,Pos,Ind,D) ->
+ render_element(Elem,State,Pos,Ind,D).
+
+
+%%% The function is the main element rendering function
+%%%
+%%% Elem: The current element to process
+%%% Stack: A stack of element names to see where we are in the dom
+%%% Pos: The current print position on the current line
+%%% Ind: How much the text should be indented after a newline
+%%% Config: The renderer's configuration
+%%%
+%%% Each element is responsible for putting new lines AFTER itself
+%%% The indents are done either by render_words when a newline happens
+%%% or when a new element is to be rendered and Pos < Ind.
+%%%
+%%% Any block elements (i.e. p, ul, li etc) are responsible for trimming
+%%% extra new lines. eg. <ul><li><p>content</p></li></ul> should only
+%%% have two newlines at the end.
+-spec render_element(Elem :: chunk_element(),
+ Stack :: [chunk_element_type()],
+ Pos :: non_neg_integer(),
+ Indent :: non_neg_integer(),
+ Config :: #config{}) ->
+ {unicode:chardata(), Pos :: non_neg_integer()}.
+
+render_element({IgnoreMe,_,Content}, State, Pos, Ind,D)
+ when IgnoreMe =:= a ->
+ render_docs(Content, State, Pos, Ind,D);
+
+%% Catch h1, h2 and h3 before the padding is done as they reset padding
+render_element({h1,_,Content},State,0 = Pos,_Ind,D) ->
+ trimnlnl(render_element({code,[],[{em,[],Content}]}, State, Pos, 0, D));
+render_element({h2,_,Content},State,0 = Pos,_Ind,D) ->
+ trimnlnl(render_element({em,[],Content}, State, Pos, 0, D));
+render_element({h3,_,Content},State,Pos,_Ind,D) when Pos =< 2 ->
+ trimnlnl(render_element({code,[],Content}, State, Pos, 2, D));
+
+render_element({pre,_Attr,_Content} = E,State,Pos,Ind,D) when Pos > Ind ->
+ %% We pad `pre` with two newlines if the previous section did not indent the region.
+ {Docs,NewPos} = render_element(E,State,0,Ind,D),
+ {["\n\n",Docs],NewPos};
+render_element({Elem,_Attr,_Content} = E,State,Pos,Ind,D) when Pos > Ind, ?IS_BLOCK(Elem) ->
+ {Docs,NewPos} = render_element(E,State,0,Ind,D),
+ {["\n",Docs],NewPos};
+render_element({'div',[{class,What}],Content},State,Pos,Ind,D) ->
+ {Docs,_} = render_docs(Content, ['div'|State], 0, Ind+2, D),
+ trimnlnl([pad(Ind - Pos),string:titlecase(What),":\n",Docs]);
+render_element({Tag,_,Content},State,Pos,Ind,D) when Tag =:= p; Tag =:= 'div' ->
+ trimnlnl(render_docs(Content, [Tag|State], Pos, Ind, D));
+
+render_element(Elem,State,Pos,Ind,D) when Pos < Ind ->
+ {Docs,NewPos} = render_element(Elem,State,Ind,Ind,D),
+ {[pad(Ind - Pos), Docs],NewPos};
+
+render_element({code,_,Content},[pre|_] = State,Pos,Ind,D) ->
+ %% When code is within a pre we don't emit any underline
+ render_docs(Content, [code|State], Pos, Ind,D);
+render_element({code,_,Content},State,Pos,Ind,D) ->
+ Underline = sansi(underline),
+ {Docs, NewPos} = render_docs(Content, [code|State], Pos, Ind,D),
+ {[Underline,Docs,ransi(underline)], NewPos};
+
+render_element({i,_,Content},State,Pos,Ind,D) ->
+ %% Just ignore i as ansi does not have cursive style
+ render_docs(Content, State, Pos, Ind,D);
+
+render_element({br,[],[]},_State,Pos,_Ind,_D) ->
+ {"",Pos};
+
+render_element({em,_,Content},State,Pos,Ind,D) ->
+ Bold = sansi(bold),
+ {Docs, NewPos} = render_docs(Content, State, Pos, Ind,D),
+ {[Bold,Docs,ransi(bold)], NewPos};
+
+render_element({pre,_,Content},State,Pos,Ind,D) ->
+ %% For pre we make sure to respect the newlines in pre
+ trimnlnl(render_docs(Content, [pre|State], Pos, Ind+2, D));
+
+render_element({ul,[{class,<<"types">>}],Content},State,_Pos,Ind,D) ->
+ {Docs, _} = render_docs(Content, [types|State], 0, Ind+2, D),
+ trimnlnl(["Types:\n", Docs]);
+render_element({li,Attr,Content},[types|_] = State,Pos,Ind,C) ->
+ Doc =
+ case {proplists:get_value(name, Attr),proplists:get_value(class, Attr)} of
+ {undefined,Class} when Class =:= undefined; Class =:= <<"type">> ->
+ %% Inline html for types
+ render_docs(Content,[type|State],Pos,Ind,C);
+ {_,<<"description">>} ->
+ %% Inline html for type descriptions
+ render_docs(Content,[type|State],Pos,Ind+2,C);
+ {Name,_} ->
+ %% Try to render from type metadata
+ case render_type_signature(binary_to_atom(Name),C) of
+ undefined when Content =:= [] ->
+ %% Failed and no content, emit place-holder
+ {["-type ",Name,"() :: term()."],0};
+ undefined ->
+ %% Failed with metadata, render the content
+ render_docs(Content,[type|State],Pos,Ind,C);
+ Type ->
+ %% Emit the erl_pp typespec
+ {Type,0}
+ end
+ end,
+ trimnl(Doc);
+render_element({ul,[],Content},State,Pos,Ind,D) ->
+ render_docs(Content, [l|State], Pos, Ind,D);
+render_element({ol,[],Content},State,Pos,Ind,D) ->
+ %% For now ul and ol does the same thing
+ render_docs(Content, [l|State], Pos, Ind,D);
+render_element({li,[],Content},[l | _] = State, Pos, Ind,D) ->
+ Bullet = get_bullet(State, D#config.encoding),
+ BulletLen = string:length(Bullet),
+ {Docs, _NewPos} = render_docs(Content, [li | State], Pos + BulletLen,Ind + BulletLen, D),
+ trimnlnl([Bullet,Docs]);
+
+render_element({dl,_,Content},State,Pos,Ind,D) ->
+ render_docs(Content, [dl|State], Pos, Ind,D);
+render_element({dt,_,Content},[dl | _] = State,Pos,Ind,D) ->
+ Underline = sansi(underline),
+ {Docs, _NewPos} = render_docs(Content, [li | State], Pos, Ind, D),
+ {[Underline,Docs,ransi(underline),":","\n"], 0};
+render_element({dd,_,Content},[dl | _] = State,Pos,Ind,D) ->
+ trimnlnl(render_docs(Content, [li | State], Pos, Ind + 2, D));
+
+render_element(B, State, Pos, Ind,#config{ columns = Cols }) when is_binary(B) ->
+ case lists:member(pre,State) of
+ true ->
+ Pre = string:replace(B,"\n",[nlpad(Ind)],all),
+ {Pre, Pos + lastline(Pre)};
+ _ ->
+ render_words(split_to_words(B),State,Pos,Ind,[[]],Cols)
+ end;
+
+render_element({Tag,Attr,Content}, State, Pos, Ind,D) ->
+ case lists:member(Tag,?ALL_ELEMENTS) of
+ true ->
+ throw({unhandled_element,Tag,Attr,Content});
+ false ->
+ %% We ignore tags that we do not care about
+ ok
+ end,
+ render_docs(Content, State, Pos, Ind,D).
+
+render_words(Words,[_,types|State],Pos,Ind,Acc,Cols) ->
+ %% When we render words and are in the types->type state we indent
+ %% the extra lines two additional spaces to make it look nice
+ render_words(Words,State,Pos,Ind+2,Acc,Cols);
+render_words([Word|T],State,Pos,Ind,Acc,Cols) when is_binary(Word) ->
+ WordLength = string:length(Word),
+ NewPos = WordLength + Pos,
+ %% We do not want to add a newline if this word is only a punctuation
+ IsPunct = is_tuple(re:run(Word,"^\\W$",[unicode])),
+ if
+ NewPos > (Cols - 10 - Ind), Word =/= <<>>, not IsPunct ->
+ %% Word does not fit, time to add a newline and also pad to Indent level
+ render_words(T,State,WordLength+Ind+1,Ind,[[[nlpad(Ind), Word]]|Acc],Cols);
+ true ->
+ %% Word does fit on line
+ [Line | LineAcc] = Acc,
+ %% Add + 1 to length for space
+ NewPosSpc = NewPos+1,
+ render_words(T,State,NewPosSpc,Ind,[[Word|Line]|LineAcc],Cols)
+ end;
+render_words([],_State,Pos,_Ind,Acc,_Cols) ->
+ Lines = lists:map(fun(RevLine) ->
+ Line = lists:reverse(RevLine),
+ lists:join($ ,Line)
+ end,lists:reverse(Acc)),
+ {iolist_to_binary(Lines), Pos}.
+
+render_type_signature(Name, #config{ docs = #docs_v1{ metadata = #{ types := AllTypes }}}) ->
+ case [Type || Type = {TName,_} <- maps:keys(AllTypes), TName =:= Name] of
+ [] ->
+ undefined;
+ Types ->
+ [erl_pp:attribute(maps:get(Type, AllTypes)) || Type <- Types]
+ end.
+
+%% Pad N spaces (and possibly pre-prend newline), disabling any ansi formatting while doing so.
+pad(N) ->
+ pad(N,"").
+nlpad(N) ->
+ %% It is important that we disable the ansi code before the new-line as otherwise the
+ %% ansi decoration may be enabled when c:paged_output tries to ask if more content
+ %% should be displayed.
+ pad(N,"\n").
+pad(N, Extra) ->
+ Pad = lists:duplicate(N," "),
+ case ansi() of
+ undefined ->
+ [Extra, Pad];
+ Ansi ->
+ ["\033[0m",Extra,Pad,Ansi]
+ end.
+
+get_bullet(_State,latin1) ->
+ <<" * ">>;
+get_bullet(State,unicode) ->
+ %% Fancy bullet point logic!
+ case length([l || l <- State]) of
+ Level when Level > 4 ->
+ get_bullet(State, latin1);
+ Level ->
+ lists:nth(Level,
+ [<<" • "/utf8>>,<<" ○ "/utf8>>,
+ <<" ◼ "/utf8>>,<<" ◻ "/utf8>>])
+ end.
+
+%% Look for the length of the last line of a string
+lastline(Str) ->
+ LastStr = case string:find(Str,"\n",trailing) of
+ nomatch ->
+ Str;
+ Match ->
+ tl(string:next_codepoint(Match))
+ end,
+ string:length(LastStr).
+
+split_to_words(B) ->
+ binary:split(B,[<<" ">>],[global]).
+
+%% These functions make sure that we trim extra newlines added
+%% by the renderer. For example if we do <li><p></p></li>
+%% that would add 4 \n at after the last </li>. This is trimmed
+%% here to only be 2 \n
+trimnlnl({Chars, _Pos}) ->
+ nl(nl(string:trim(Chars, trailing, "\n")));
+trimnlnl(Chars) ->
+ nl(nl(string:trim(Chars, trailing, "\n"))).
+trimnl({Chars, _Pos}) ->
+ nl(string:trim(Chars, trailing, "\n")).
+nl({Chars, _Pos}) ->
+ nl(Chars);
+nl(Chars) ->
+ {[Chars,"\n"],0}.
+
+%% We keep the current ansi state in the pdict so that we know
+%% what to disable and enable when doing padding
+init_ansi(#config{ ansi = undefined, io_opts = Opts }) ->
+ %% We use this as our heuristic to see if we should print ansi or not
+ case {application:get_env(kernel, shell_docs_ansi),
+ proplists:is_defined(echo, Opts) andalso
+ proplists:is_defined(expand_fun, Opts),
+ os:type()} of
+ {{ok,false}, _, _} ->
+ put(ansi, noansi);
+ {{ok,true}, _, _} ->
+ put(ansi, []);
+ {_, _, {win32,_}} ->
+ put(ansi, noansi);
+ {_, true,_} ->
+ put(ansi, []);
+ {_, false,_} ->
+ put(ansi, noansi)
+ end;
+init_ansi(#config{ ansi = true }) ->
+ put(ansi, []);
+init_ansi(#config{ ansi = false }) ->
+ put(ansi, noansi).
+
+
+
+clean_ansi() ->
+ case get(ansi) of
+ [] -> erase(ansi);
+ noansi -> erase(ansi)
+ end,
+ ok.
+
+%% Set ansi
+sansi(Type) -> sansi(Type, get(ansi)).
+sansi(_Type, noansi) ->
+ [];
+sansi(Type, Curr) ->
+ put(ansi,[Type | Curr]),
+ ansi(get(ansi)).
+
+%% Clear ansi
+ransi(Type) -> ransi(Type, get(ansi)).
+ransi(_Type, noansi) ->
+ [];
+ransi(Type, Curr) ->
+ put(ansi,proplists:delete(Type,Curr)),
+ case ansi(get(ansi)) of
+ undefined ->
+ "\033[0m";
+ Ansi ->
+ Ansi
+ end.
+
+ansi() -> ansi(get(ansi)).
+ansi(noansi) -> undefined;
+ansi(Curr) ->
+ case lists:usort(Curr) of
+ [] ->
+ undefined;
+ [bold] ->
+ "\033[;1m";
+ [underline] ->
+ "\033[;;4m";
+ [bold,underline] ->
+ "\033[;1;4m"
+ end.
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index f1a1afaf72..b59e3b28c0 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -92,6 +92,7 @@
sets,
shell,
shell_default,
+ shell_docs,
slave,
sofs,
string,
@@ -108,6 +109,6 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.7.1","crypto-3.3",
+ {runtime_dependencies, ["sasl-3.0","kernel-7.0","erts-11.0","crypto-3.3",
"compiler-5.0"]}
]}.
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 6d6ee14d29..bba0d1ceee 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -21,6 +21,7 @@
%% versions from the following OTP releases:
%% - OTP 21
%% - OTP 22
+%% - OTP 23
%%
%% We also allow upgrade from, and downgrade to all
%% versions that have branched off from the above
@@ -35,6 +36,13 @@
{<<"^3\\.11\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.12$">>,[restart_new_emulator]},
{<<"^3\\.12\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.12\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.13$">>,[restart_new_emulator]},
+ {<<"^3\\.13\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.13\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.13\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.14$">>,[restart_new_emulator]},
+ {<<"^3\\.14\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.5$">>,[restart_new_emulator]},
{<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
@@ -59,6 +67,13 @@
{<<"^3\\.11\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.12$">>,[restart_new_emulator]},
{<<"^3\\.12\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.12\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.13$">>,[restart_new_emulator]},
+ {<<"^3\\.13\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.13\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.13\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.14$">>,[restart_new_emulator]},
+ {<<"^3\\.14\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.5$">>,[restart_new_emulator]},
{<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 1ac7334830..7295a2e08e 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -32,6 +32,9 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, format_status/2]).
+%% logger callback
+-export([format_log/1, format_log/2]).
+
%% For release_handler only
-export([get_callback_module/1]).
@@ -44,14 +47,17 @@
{reason,Reason},
{offender,extract_child(Child)}]},
#{domain=>[otp,sasl],
- report_cb=>fun logger:format_otp_report/1,
+ report_cb=>fun supervisor:format_log/2,
logger_formatter=>#{title=>"SUPERVISOR REPORT"},
error_logger=>#{tag=>error_report,
- type=>supervisor_report}})).
+ type=>supervisor_report,
+ report_cb=>fun supervisor:format_log/1}})).
%%--------------------------------------------------------------------------
--export_type([sup_flags/0, child_spec/0, startchild_ret/0, strategy/0]).
+-export_type([sup_flags/0, child_spec/0, strategy/0,
+ startchild_ret/0, startchild_err/0,
+ startlink_ret/0, startlink_err/0]).
%%--------------------------------------------------------------------------
@@ -122,7 +128,7 @@
strategy :: strategy() | 'undefined',
children = {[],#{}} :: children(), % Ids in start order
dynamics :: {'maps', #{pid() => list()}}
- | {'sets', sets:set(pid())}
+ | {'mapsets', #{pid() => []}}
| 'undefined',
intensity :: non_neg_integer() | 'undefined',
period :: pos_integer() | 'undefined',
@@ -924,21 +930,21 @@ monitor_child(Pid) ->
terminate_dynamic_children(State) ->
Child = get_dynamic_child(State),
{Pids, EStack0} = monitor_dynamic_children(Child,State),
- Sz = sets:size(Pids),
+ Sz = maps:size(Pids),
EStack = case Child#child.shutdown of
brutal_kill ->
- sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
+ maps:fold(fun(P, _, _) -> exit(P, kill) end, ok, Pids),
wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
infinity ->
- sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
+ maps:fold(fun(P, _, _) -> exit(P, shutdown) end, ok, Pids),
wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
Time ->
- sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
+ maps:fold(fun(P, _, _) -> exit(P, shutdown) end, ok, Pids),
TRef = erlang:start_timer(Time, self(), kill),
wait_dynamic_children(Child, Pids, Sz, TRef, EStack0)
end,
%% Unroll stacked errors and report them
- dict:fold(fun(Reason, Ls, _) ->
+ maps:fold(fun(Reason, Ls, _) ->
?report_error(shutdown_error, Reason,
Child#child{pid=Ls}, State#state.name)
end, ok, EStack).
@@ -947,15 +953,15 @@ monitor_dynamic_children(Child,State) ->
dyn_fold(fun(P,{Pids, EStack}) when is_pid(P) ->
case monitor_child(P) of
ok ->
- {sets:add_element(P, Pids), EStack};
+ {maps:put(P, P, Pids), EStack};
{error, normal} when not (?is_permanent(Child)) ->
{Pids, EStack};
{error, Reason} ->
- {Pids, dict:append(Reason, P, EStack)}
+ {Pids, maps_prepend(Reason, P, EStack)}
end;
(?restarting(_), {Pids, EStack}) ->
{Pids, EStack}
- end, {sets:new(), dict:new()}, State).
+ end, {maps:new(), maps:new()}, State).
wait_dynamic_children(_Child, _Pids, 0, undefined, EStack) ->
EStack;
@@ -973,36 +979,44 @@ wait_dynamic_children(#child{shutdown=brutal_kill} = Child, Pids, Sz,
TRef, EStack) ->
receive
{'DOWN', _MRef, process, Pid, killed} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
TRef, EStack);
{'DOWN', _MRef, process, Pid, Reason} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
- TRef, dict:append(Reason, Pid, EStack))
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
+ TRef, maps_prepend(Reason, Pid, EStack))
end;
wait_dynamic_children(Child, Pids, Sz, TRef, EStack) ->
receive
{'DOWN', _MRef, process, Pid, shutdown} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
TRef, EStack);
{'DOWN', _MRef, process, Pid, {shutdown, _}} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
TRef, EStack);
{'DOWN', _MRef, process, Pid, normal} when not (?is_permanent(Child)) ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
TRef, EStack);
{'DOWN', _MRef, process, Pid, Reason} ->
- wait_dynamic_children(Child, sets:del_element(Pid, Pids), Sz-1,
- TRef, dict:append(Reason, Pid, EStack));
+ wait_dynamic_children(Child, maps:remove(Pid, Pids), Sz-1,
+ TRef, maps_prepend(Reason, Pid, EStack));
{timeout, TRef, kill} ->
- sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
+ maps:fold(fun(P, _, _) -> exit(P, kill) end, ok, Pids),
wait_dynamic_children(Child, Pids, Sz, undefined, EStack)
end.
+maps_prepend(Key, Value, Map) ->
+ case maps:find(Key, Map) of
+ {ok, Values} ->
+ maps:put(Key, [Value|Values], Map);
+ error ->
+ maps:put(Key, [Value], Map)
+ end.
+
%%-----------------------------------------------------------------
%% Access #state.children
%%-----------------------------------------------------------------
@@ -1420,9 +1434,159 @@ report_progress(Child, SupName) ->
report=>[{supervisor,SupName},
{started,extract_child(Child)}]},
#{domain=>[otp,sasl],
- report_cb=>fun logger:format_otp_report/1,
+ report_cb=>fun supervisor:format_log/2,
logger_formatter=>#{title=>"PROGRESS REPORT"},
- error_logger=>#{tag=>info_report,type=>progress}}).
+ error_logger=>#{tag=>info_report,
+ type=>progress,
+ report_cb=>fun supervisor:format_log/1}}).
+
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(LogReport) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(LogReport, Depth), FormatOpts).
+
+limit_report(LogReport, unlimited) ->
+ LogReport;
+limit_report(#{label:={supervisor,progress},
+ report:=[{supervisor,_}=Supervisor,{started,Child}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Supervisor,
+ {started,limit_child_report(Child, Depth)}]};
+limit_report(#{label:={supervisor,_Error},
+ report:=[{supervisor,_}=Supervisor,{errorContext,Ctxt},
+ {reason,Reason},{offender,Child}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Supervisor,
+ {errorContext,io_lib:limit_term(Ctxt, Depth)},
+ {reason,io_lib:limit_term(Reason, Depth)},
+ {offender,limit_child_report(Child, Depth)}]}.
+
+limit_child_report(Report, Depth) ->
+ io_lib:limit_term(Report, Depth).
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default, FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={supervisor,progress},
+ report:=[{supervisor,SupName},{started,Child}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ {ChildFormat,ChildArgs} = format_child_log_single(Child, "Started:"),
+ Format = "Supervisor: "++P++".",
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName];
+ _ ->
+ [SupName,Depth]
+ end,
+ {Format++ChildFormat,Args++ChildArgs};
+format_log_single(#{label:={supervisor,_Error},
+ report:=[{supervisor,SupName},
+ {errorContext,Ctxt},
+ {reason,Reason},
+ {offender,Child}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Supervisor: ",P,". Context: ",P,
+ ". Reason: ",P,"."]),
+ {ChildFormat,ChildArgs} = format_child_log_single(Child, "Offender:"),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Ctxt,Reason];
+ _ ->
+ [SupName,Depth,Ctxt,Depth,Reason,Depth]
+ end,
+ {Format++ChildFormat,Args++ChildArgs};
+format_log_single(Report,FormatOpts) ->
+ format_log_multi(Report,FormatOpts).
+
+format_log_multi(#{label:={supervisor,progress},
+ report:=[{supervisor,SupName},
+ {started,Child}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" supervisor: ",P,"~n",
+ " started: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Child];
+ _ ->
+ [SupName,Depth,Child,Depth]
+ end,
+ {Format,Args};
+format_log_multi(#{label:={supervisor,_Error},
+ report:=[{supervisor,SupName},
+ {errorContext,Ctxt},
+ {reason,Reason},
+ {offender,Child}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" supervisor: ",P,"~n",
+ " errorContext: ",P,"~n",
+ " reason: ",P,"~n",
+ " offender: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Ctxt,Reason,Child];
+ _ ->
+ [SupName,Depth,Ctxt,Depth,Reason,Depth,Child,Depth]
+ end,
+ {Format,Args}.
+
+format_child_log_single(Child, Tag) ->
+ {id,Id} = lists:keyfind(id, 1, Child),
+ case lists:keyfind(pid, 1, Child) of
+ false ->
+ {nb_children,NumCh} = lists:keyfind(nb_children, 1, Child),
+ {" ~s id=~w,nb_children=~w.", [Tag,Id,NumCh]};
+ T when is_tuple(T) ->
+ {pid,Pid} = lists:keyfind(pid, 1, Child),
+ {" ~s id=~w,pid=~w.", [Tag,Id,Pid]}
+ end.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
format_status(terminate, [_PDict, State]) ->
State;
@@ -1431,36 +1595,41 @@ format_status(_, [_PDict, State]) ->
{supervisor, [{"Callback", State#state.module}]}].
%%%-----------------------------------------------------------------
-%%% Dynamics database access
-dyn_size(#state{dynamics = {Mod,Db}}) ->
- Mod:size(Db).
-
-dyn_erase(Pid,#state{dynamics={sets,Db}}=State) ->
- State#state{dynamics={sets,sets:del_element(Pid,Db)}};
-dyn_erase(Pid,#state{dynamics={maps,Db}}=State) ->
- State#state{dynamics={maps,maps:remove(Pid,Db)}}.
-
-dyn_store(Pid,_,#state{dynamics={sets,Db}}=State) ->
- State#state{dynamics={sets,sets:add_element(Pid,Db)}};
-dyn_store(Pid,Args,#state{dynamics={maps,Db}}=State) ->
- State#state{dynamics={maps,Db#{Pid => Args}}}.
-
-dyn_fold(Fun,Init,#state{dynamics={sets,Db}}) ->
- sets:fold(Fun,Init,Db);
-dyn_fold(Fun,Init,#state{dynamics={maps,Db}}) ->
+%%% Dynamics database access.
+%%%
+%%% Store all dynamic children in a map with the pid as the key. If
+%%% the children are permanent, store the start arguments as the value,
+%%% otherwise store [] as the value.
+%%%
+
+dyn_size(#state{dynamics = {_Kind,Db}}) ->
+ map_size(Db).
+
+dyn_erase(Pid,#state{dynamics={Kind,Db}}=State) ->
+ State#state{dynamics={Kind,maps:remove(Pid,Db)}}.
+
+dyn_store(Pid,Args,#state{dynamics={Kind,Db}}=State) ->
+ case Kind of
+ mapsets ->
+ %% Children are temporary. The start arguments
+ %% will not be needed again. Store [].
+ State#state{dynamics={mapsets,Db#{Pid => []}}};
+ maps ->
+ %% Children are permanent and may be restarted.
+ %% Store the start arguments.
+ State#state{dynamics={maps,Db#{Pid => Args}}}
+ end.
+
+dyn_fold(Fun,Init,#state{dynamics={_Kind,Db}}) ->
maps:fold(fun(Pid,_,Acc) -> Fun(Pid,Acc) end, Init, Db).
-dyn_map(Fun, #state{dynamics={sets,Db}}) ->
- lists:map(Fun, sets:to_list(Db));
-dyn_map(Fun, #state{dynamics={maps,Db}}) ->
+dyn_map(Fun, #state{dynamics={_Kind,Db}}) ->
lists:map(Fun, maps:keys(Db)).
-dyn_exists(Pid, #state{dynamics={sets, Db}}) ->
- sets:is_element(Pid, Db);
-dyn_exists(Pid, #state{dynamics={maps, Db}}) ->
- maps:is_key(Pid, Db).
+dyn_exists(Pid, #state{dynamics={_Kind, Db}}) ->
+ is_map_key(Pid, Db).
-dyn_args(_Pid, #state{dynamics={sets, _Db}}) ->
+dyn_args(_Pid, #state{dynamics={mapsets, _Db}}) ->
{ok,undefined};
dyn_args(Pid, #state{dynamics={maps, Db}}) ->
maps:find(Pid, Db).
@@ -1469,6 +1638,6 @@ dyn_init(State) ->
dyn_init(get_dynamic_child(State),State).
dyn_init(Child,State) when ?is_temporary(Child) ->
- State#state{dynamics={sets,sets:new()}};
+ State#state{dynamics={mapsets,maps:new()}};
dyn_init(_Child,State) ->
State#state{dynamics={maps,maps:new()}}.
diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl
index 21ba6f53af..abbfb404a5 100644
--- a/lib/stdlib/src/supervisor_bridge.erl
+++ b/lib/stdlib/src/supervisor_bridge.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,6 +28,8 @@
%% Internal exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
-export([code_change/3]).
+%% logger callback
+-export([format_log/1, format_log/2]).
-callback init(Args :: term()) ->
{ok, Pid :: pid(), State :: term()} | ignore | {error, Error :: term()}.
@@ -136,9 +138,12 @@ report_progress(Pid, Mod, StartArgs, SupName) ->
{started, [{pid, Pid},
{mfa, {Mod, init, [StartArgs]}}]}]},
#{domain=>[otp,sasl],
- report_cb=>fun logger:format_otp_report/1,
+ report_cb=>fun supervisor_bridge:format_log/2,
logger_formatter=>#{title=>"PROGRESS REPORT"},
- error_logger=>#{tag=>info_report,type=>progress}}).
+ error_logger=>#{tag=>info_report,
+ type=>progress,
+ report_cb=>
+ fun supervisor_bridge:format_log/1}}).
report_error(Error, Reason, #state{name = Name, pid = Pid, mod = Mod}) ->
?LOG_ERROR(#{label=>{supervisor,error},
@@ -147,6 +152,167 @@ report_error(Error, Reason, #state{name = Name, pid = Pid, mod = Mod}) ->
{reason, Reason},
{offender, [{pid, Pid}, {mod, Mod}]}]},
#{domain=>[otp,sasl],
- report_cb=>fun logger:format_otp_report/1,
+ report_cb=>fun supervisor_bridge:format_log/2,
logger_formatter=>#{title=>"SUPERVISOR REPORT"},
- error_logger=>#{tag=>error_report,type=>supervisor_report}}).
+ error_logger=>#{tag=>error_report,
+ type=>supervisor_report,
+ report_cb=>
+ fun supervisor_bridge:format_log/1}}).
+
+%% format_log/1 is the report callback used by Logger handler
+%% error_logger only. It is kept for backwards compatibility with
+%% legacy error_logger event handlers. This function must always
+%% return {Format,Args} compatible with the arguments in this module's
+%% calls to error_logger prior to OTP-21.0.
+format_log(LogReport) ->
+ Depth = error_logger:get_format_depth(),
+ FormatOpts = #{chars_limit => unlimited,
+ depth => Depth,
+ single_line => false,
+ encoding => utf8},
+ format_log_multi(limit_report(LogReport, Depth), FormatOpts).
+
+limit_report(LogReport, unlimited) ->
+ LogReport;
+limit_report(#{label:={supervisor,progress},
+ report:=[{supervisor,_}=Supervisor,{started,Child}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Supervisor,
+ {started,limit_child_report(Child, Depth)}]};
+limit_report(#{label:={supervisor,error},
+ report:=[{supervisor,_}=Supervisor,{errorContext,Ctxt},
+ {reason,Reason},{offender,Child}]}=LogReport,
+ Depth) ->
+ LogReport#{report=>[Supervisor,
+ {errorContext,io_lib:limit_term(Ctxt, Depth)},
+ {reason,io_lib:limit_term(Reason, Depth)},
+ {offender,io_lib:limit_term(Child, Depth)}]}.
+
+limit_child_report(ChildReport, Depth) ->
+ {mfa,{M,F,[As]}} = lists:keyfind(mfa, 1, ChildReport),
+ NewMFAs = {M,F,[io_lib:limit_term(As, Depth)]},
+ lists:keyreplace(mfa, 1, ChildReport, {mfa,NewMFAs}).
+
+%% format_log/2 is the report callback for any Logger handler, except
+%% error_logger.
+format_log(Report, FormatOpts0) ->
+ Default = #{chars_limit => unlimited,
+ depth => unlimited,
+ single_line => false,
+ encoding => utf8},
+ FormatOpts = maps:merge(Default, FormatOpts0),
+ IoOpts =
+ case FormatOpts of
+ #{chars_limit:=unlimited} ->
+ [];
+ #{chars_limit:=Limit} ->
+ [{chars_limit,Limit}]
+ end,
+ {Format,Args} = format_log_single(Report, FormatOpts),
+ io_lib:format(Format, Args, IoOpts).
+
+format_log_single(#{label:={supervisor,progress},
+ report:=[{supervisor,SupName},{started,Child}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ {ChildFormat,ChildArgs} =
+ format_child_log_progress_single(Child, "Started:", FormatOpts),
+ Format = "Supervisor: "++P++".",
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName];
+ _ ->
+ [SupName,Depth]
+ end,
+ {Format++ChildFormat,Args++ChildArgs};
+format_log_single(#{label:={supervisor,_Error},
+ report:=[{supervisor,SupName},
+ {errorContext,Ctxt},
+ {reason,Reason},
+ {offender,Child}]},
+ #{single_line:=true,depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format = lists:append(["Supervisor: ",P,". Context: ",P,
+ ". Reason: ",P,"."]),
+ {ChildFormat,ChildArgs} =
+ format_child_log_error_single(Child, "Offender:"),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Ctxt,Reason];
+ _ ->
+ [SupName,Depth,Ctxt,Depth,Reason,Depth]
+ end,
+ {Format++ChildFormat,Args++ChildArgs};
+format_log_single(Report, FormatOpts) ->
+ format_log_multi(Report, FormatOpts).
+
+format_log_multi(#{label:={supervisor,progress},
+ report:=[{supervisor,SupName},
+ {started,Child}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" supervisor: ",P,"~n",
+ " started: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Child];
+ _ ->
+ [SupName,Depth,Child,Depth]
+ end,
+ {Format,Args};
+format_log_multi(#{label:={supervisor,_Error},
+ report:=[{supervisor,SupName},
+ {errorContext,Ctxt},
+ {reason,Reason},
+ {offender,Child}]},
+ #{depth:=Depth}=FormatOpts) ->
+ P = p(FormatOpts),
+ Format =
+ lists:append(
+ [" supervisor: ",P,"~n",
+ " errorContext: ",P,"~n",
+ " reason: ",P,"~n",
+ " offender: ",P,"~n"]),
+ Args =
+ case Depth of
+ unlimited ->
+ [SupName,Ctxt,Reason,Child];
+ _ ->
+ [SupName,Depth,Ctxt,Depth,Reason,Depth,Child,Depth]
+ end,
+ {Format,Args}.
+
+format_child_log_progress_single(Child, Tag, FormatOpts) ->
+ {pid,Pid} = lists:keyfind(pid, 1, Child),
+ {mfa,MFAs} = lists:keyfind(mfa, 1, Child),
+ Args =
+ case maps:get(depth, FormatOpts) of
+ unlimited ->
+ [MFAs];
+ Depth ->
+ [MFAs, Depth]
+ end,
+ {" ~s pid=~w,mfa="++p(FormatOpts)++".",[Tag,Pid]++Args}.
+
+format_child_log_error_single(Child, Tag) ->
+ {pid,Pid} = lists:keyfind(pid, 1, Child),
+ {mod,Mod} = lists:keyfind(mod, 1, Child),
+ {" ~s pid=~w,mod=~w.",[Tag,Pid,Mod]}.
+
+p(#{single_line:=Single,depth:=Depth,encoding:=Enc}) ->
+ "~"++single(Single)++mod(Enc)++p(Depth);
+p(unlimited) ->
+ "p";
+p(_Depth) ->
+ "P".
+
+single(true) -> "0";
+single(false) -> "".
+
+mod(latin1) -> "";
+mod(_) -> "t".
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index 93bf4743d2..e803b749f7 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -31,7 +31,10 @@
install/2, install/3, remove/2, remove/3]).
-export([handle_system_msg/6, handle_system_msg/7, handle_debug/4,
print_log/1, get_log/1, get_debug/3, debug_options/1, suspend_loop_hib/6]).
--deprecated([{get_debug,3,eventually}]).
+
+-deprecated([{get_debug,3,
+ "incorrectly documented and only for internal use. Can often "
+ "be replaced with sys:get_log/1"}]).
%%-----------------------------------------------------------------
%% Types
diff --git a/lib/stdlib/src/uri_string.erl b/lib/stdlib/src/uri_string.erl
index 2ee0ba53b9..3060f2bfaa 100644
--- a/lib/stdlib/src/uri_string.erl
+++ b/lib/stdlib/src/uri_string.erl
@@ -226,10 +226,21 @@
%%-------------------------------------------------------------------------
%% External API
%%-------------------------------------------------------------------------
--export([compose_query/1, compose_query/2,
- dissect_query/1, normalize/1, normalize/2, parse/1,
- recompose/1, resolve/2, resolve/3, transcode/2]).
--export_type([error/0, uri_map/0, uri_string/0]).
+-export([allowed_characters/0,
+ compose_query/1,
+ compose_query/2,
+ dissect_query/1,
+ normalize/1,
+ normalize/2,
+ percent_decode/1,
+ parse/1,
+ recompose/1,
+ resolve/2,
+ resolve/3,
+ transcode/2]).
+-export_type([error/0,
+ uri_map/0,
+ uri_string/0]).
%%-------------------------------------------------------------------------
@@ -286,7 +297,7 @@
port => non_neg_integer() | undefined,
query => unicode:chardata(),
scheme => unicode:chardata(),
- userinfo => unicode:chardata()} | #{}.
+ userinfo => unicode:chardata()}.
%%-------------------------------------------------------------------------
@@ -453,6 +464,61 @@ transcode(URIString, Options) when is_list(URIString) ->
%%-------------------------------------------------------------------------
+%% Misc
+%%-------------------------------------------------------------------------
+-spec allowed_characters() -> [{atom(), list()}].
+allowed_characters() ->
+ Input = lists:seq(0,127),
+ Scheme = lists:filter(fun is_scheme/1, Input),
+ UserInfo = lists:filter(fun is_userinfo/1, Input),
+ Host = lists:filter(fun is_host/1, Input),
+ IPv4 = lists:filter(fun is_ipv4/1, Input),
+ IPv6 = lists:filter(fun is_ipv6/1, Input),
+ RegName = lists:filter(fun is_reg_name/1, Input),
+ Path = lists:filter(fun is_path/1, Input),
+ Query = lists:filter(fun is_query/1, Input),
+ Fragment = lists:filter(fun is_fragment/1, Input),
+ Reserved = lists:filter(fun is_reserved/1, Input),
+ Unreserved = lists:filter(fun is_unreserved/1, Input),
+ [{scheme, Scheme},
+ {userinfo, UserInfo},
+ {host, Host},
+ {ipv4, IPv4},
+ {ipv6, IPv6},
+ {regname,RegName},
+ {path,Path},
+ {query, Query},
+ {fragment,Fragment},
+ {reserved, Reserved},
+ {unreserved, Unreserved}].
+
+-spec percent_decode(URI) -> Result when
+ URI :: uri_string() | uri_map(),
+ Result :: uri_string() |
+ uri_map() |
+ {error, {invalid, {atom(), {term(), term()}}}}.
+percent_decode(URIMap) when is_map(URIMap)->
+ Fun = fun (K,V) when K =:= userinfo; K =:= host; K =:= path;
+ K =:= query; K =:= fragment ->
+ case raw_decode(V) of
+ {error, Reason, Input} ->
+ throw({error, {invalid, {K, {Reason, Input}}}});
+ Else ->
+ Else
+ end;
+ %% Handle port and scheme
+ (_,V) ->
+ V
+ end,
+ try maps:map(Fun, URIMap)
+ catch throw:Return ->
+ Return
+ end;
+percent_decode(URI) when is_list(URI) orelse
+ is_binary(URI) ->
+ raw_decode(URI).
+
+%%-------------------------------------------------------------------------
%% Functions for working with the query part of a URI as a list
%% of key/value pairs.
%% HTML 5.2 - 4.10.21.6 URL-encoded form data - WHATWG URL (10 Jan 2018) - UTF-8
@@ -1421,8 +1487,15 @@ decode(<<$%,C0,C1,Cs/binary>>, Acc) ->
case is_hex_digit(C0) andalso is_hex_digit(C1) of
true ->
B = ?HEX2DEC(C0)*16+?HEX2DEC(C1),
- case is_reserved(B) of
- true ->
+ %% [2.4] When a URI is dereferenced, the components and subcomponents
+ %% significant to the scheme-specific dereferencing process (if any)
+ %% must be parsed and separated before the percent-encoded octets within
+ %% those components can be safely decoded, as otherwise the data may be
+ %% mistaken for component delimiters. The only exception is for
+ %% percent-encoded octets corresponding to characters in the unreserved
+ %% set, which can be decoded at any time.
+ case is_unreserved(B) of
+ false ->
%% [2.2] Characters in the reserved set are protected from
%% normalization.
%% [2.1] For consistency, URI producers and normalizers should
@@ -1431,7 +1504,7 @@ decode(<<$%,C0,C1,Cs/binary>>, Acc) ->
H0 = hex_to_upper(C0),
H1 = hex_to_upper(C1),
decode(Cs, <<Acc/binary,$%,H0,H1>>);
- false ->
+ true ->
decode(Cs, <<Acc/binary, B>>)
end;
false -> throw({error,invalid_percent_encoding,<<$%,C0,C1>>})
@@ -1441,6 +1514,32 @@ decode(<<C,Cs/binary>>, Acc) ->
decode(<<>>, Acc) ->
check_utf8(Acc).
+-spec raw_decode(list()|binary()) -> list() | binary() | error().
+raw_decode(Cs) ->
+ raw_decode(Cs, <<>>).
+%%
+raw_decode(L, Acc) when is_list(L) ->
+ try
+ B0 = unicode:characters_to_binary(L),
+ B1 = raw_decode(B0, Acc),
+ unicode:characters_to_list(B1)
+ catch
+ throw:{error, Atom, RestData} ->
+ {error, Atom, RestData}
+ end;
+raw_decode(<<$%,C0,C1,Cs/binary>>, Acc) ->
+ case is_hex_digit(C0) andalso is_hex_digit(C1) of
+ true ->
+ B = ?HEX2DEC(C0)*16+?HEX2DEC(C1),
+ raw_decode(Cs, <<Acc/binary, B>>);
+ false ->
+ throw({error,invalid_percent_encoding,<<$%,C0,C1>>})
+ end;
+raw_decode(<<C,Cs/binary>>, Acc) ->
+ raw_decode(Cs, <<Acc/binary, C>>);
+raw_decode(<<>>, Acc) ->
+ check_utf8(Acc).
+
%% Returns Cs if it is utf8 encoded.
check_utf8(Cs) ->
case unicode:characters_to_list(Cs) of
@@ -1679,6 +1778,11 @@ update_port(#{}, URI) ->
update_path(#{path := Path}, empty) ->
encode_path(Path);
+update_path(#{host := _, path := Path0}, URI) ->
+ %% When host is present in a URI the path must begin with "/" or be empty.
+ Path1 = maybe_flatten_list(Path0),
+ Path = make_path_absolute(Path1),
+ concat(URI,encode_path(Path));
update_path(#{path := Path}, URI) ->
concat(URI,encode_path(Path));
update_path(#{}, empty) ->
@@ -1756,6 +1860,35 @@ maybe_to_list(Comp) -> Comp.
encode_port(Port) ->
integer_to_binary(Port).
+%% URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+%%
+%% hier-part = "//" authority path-abempty
+%% / path-absolute
+%% / path-rootless
+%% / path-empty
+%%
+%% path = path-abempty ; begins with "/" or is empty
+%% / path-absolute ; begins with "/" but not "//"
+%% / path-noscheme ; begins with a non-colon segment
+%% / path-rootless ; begins with a segment
+%% / path-empty ; zero characters
+make_path_absolute(<<>>) ->
+ <<>>;
+make_path_absolute("") ->
+ "";
+make_path_absolute(<<"/",_/binary>> = Path) ->
+ Path;
+make_path_absolute([$/|_] = Path) ->
+ Path;
+make_path_absolute(Path) when is_binary(Path) ->
+ concat(<<$/>>, Path);
+make_path_absolute(Path) when is_list(Path) ->
+ concat("/", Path).
+
+maybe_flatten_list(Path) when is_binary(Path) ->
+ Path;
+maybe_flatten_list(Path) ->
+ unicode:characters_to_list(Path).
%%-------------------------------------------------------------------------
%% Helper functions for resolve
@@ -1869,7 +2002,7 @@ transcode_pct([], Acc, B, InEncoding, OutEncoding) ->
OutBinary = convert_to_binary(B, InEncoding, OutEncoding),
PctEncUtf8 = percent_encode_segment(OutBinary),
Out = convert_to_list(PctEncUtf8, utf8),
- lists:reverse(Acc) ++ Out.
+ lists:reverse(Acc, Out).
%% Convert to binary
@@ -1904,7 +2037,7 @@ flatten_list(L, InEnc) ->
%%
flatten_list([H|T], InEnc, Acc) when is_binary(H) ->
L = convert_to_list(H, InEnc),
- flatten_list(T, InEnc, lists:reverse(L) ++ Acc);
+ flatten_list(T, InEnc, lists:reverse(L, Acc));
flatten_list([H|T], InEnc, Acc) when is_list(H) ->
flatten_list(H ++ T, InEnc, Acc);
flatten_list([H|T], InEnc, Acc) ->
@@ -1924,7 +2057,7 @@ percent_encode_segment(Segment) ->
%%-------------------------------------------------------------------------
%% Returns separator to be used between key-value pairs
-get_separator(L) when length(L) =:= 0 ->
+get_separator([]) ->
<<>>;
get_separator(_L) ->
<<"&">>.
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index a922bf3fbe..8f703d9d1a 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -53,6 +53,10 @@
%% for debugging, to turn off catch
-define(CATCH, catch).
+%% Debug.
+-define(SHOW_GP_BIT_11(B, F), ok).
+%%-define(SHOW_GP_BIT_11(B, F), io:format("F = ~.16#, B = ~lp\n", [F, B])).
+
%% option sets
-record(unzip_opts, {
output, % output object (fun)
@@ -138,6 +142,10 @@
-define(PKWARE_RESERVED, 11).
-define(BZIP2_COMPRESSED, 12).
+%% Version 2.0, attribute compatibility type 3 (Unix)
+-define(VERSION_MADE_BY, 20 bor (3 bsl 8)).
+-define(GP_BIT_11, 16#800). % Filename and file comment UTF-8 encoded.
+
%% zip-file records
-define(LOCAL_FILE_MAGIC,16#04034b50).
-define(LOCAL_FILE_HEADER_SZ,(4+2+2+2+2+2+4+4+4+2+2)).
@@ -160,6 +168,7 @@
-define(CENTRAL_DIR_DIGITAL_SIG_MAGIC, 16#05054b50).
-define(CENTRAL_DIR_DIGITAL_SIG_SZ, (4+2)).
+-define(CENTRAL_FILE_EXT_ATTRIBUTES, 8#644 bsl 16).
-define(CENTRAL_FILE_MAGIC, 16#02014b50).
-record(cd_file_header, {version_made_by,
@@ -191,12 +200,16 @@
zip_comment_length}).
--type create_option() :: memory | cooked | verbose | {comment, string()}
- | {cwd, file:filename()}
- | {compress, extension_spec()}
- | {uncompress, extension_spec()}.
+-type create_option() :: memory | cooked | verbose
+ | {comment, Comment ::string()}
+ | {cwd, CWD :: file:filename()}
+ | {compress, What :: extension_spec()}
+ | {uncompress, What :: extension_spec()}.
-type extension() :: string().
--type extension_spec() :: all | [extension()] | {add, [extension()]} | {del, [extension()]}.
+-type extension_spec() :: all
+ | [Extension :: extension()]
+ | {add, [Extension :: extension()]}
+ | {del, [Extension :: extension()]}.
-type filename() :: file:filename().
-type zip_comment() :: #zip_comment{}.
@@ -277,8 +290,11 @@ do_openzip_get(_, _) ->
throw(einval).
file_name_search(Name,Files) ->
- case lists:dropwhile(fun({ZipFile,_}) -> ZipFile#zip_file.name =/= Name end,
- Files) of
+ Fun = fun({ZipFile,_}) ->
+ not string:equal(ZipFile#zip_file.name, Name,
+ _IgnoreCase = false, _Norm = nfc)
+ end,
+ case lists:dropwhile(Fun, Files) of
[ZFile|_] -> ZFile;
[] -> false
end.
@@ -429,12 +445,7 @@ zip(F, Files) -> zip(F, Files, []).
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 :: file:filename(),
+ Option :: create_option(),
RetValue :: {ok, FileName :: file:name()}
| {ok, {FileName :: file:name(), binary()}}
| {error, Reason :: term()}).
@@ -622,9 +633,11 @@ get_zip_opt([Unknown | _Rest], _Opts) ->
%% feedback funs
silent(_) -> ok.
-verbose_unzip(FN) -> io:format("extracting: ~tp\n", [FN]).
+verbose_unzip(FN) ->
+ io:format("extracting: ~ts\n", [io_lib:write_string(FN)]).
-verbose_zip(FN) -> io:format("adding: ~tp\n", [FN]).
+verbose_zip(FN) ->
+ io:format("adding: ~ts\n", [io_lib:write_string(FN)]).
%% file filter funs
all(_) -> true.
@@ -655,7 +668,10 @@ get_zip_options(Files, Options) ->
compress = all,
uncompress = Suffixes
},
- get_zip_opt(Options, Opts).
+ Opts1 = #zip_opts{comment = Comment} = get_zip_opt(Options, Opts),
+ %% UTF-8 encode characters in the interval from 127 to 255.
+ {Comment1, _} = encode_string(Comment),
+ Opts1#zip_opts{comment = Comment1}.
get_unzip_options(F, Options) ->
Opts = #unzip_opts{file_filter = fun all/1,
@@ -850,16 +866,18 @@ put_z_files([F | Rest], Z, Out0, Pos0,
regular -> FileInfo#file_info.size;
directory -> 0
end,
- FileName = get_filename(F, Type),
+ FileName0 = get_filename(F, Type),
+ %% UTF-8 encode characters in the interval from 127 to 255.
+ {FileName, GPFlag} = encode_string(FileName0),
CompMethod = get_comp_method(FileName, UncompSize, Opts, Type),
- LH = local_file_header_from_info_method_name(FileInfo, UncompSize, CompMethod, FileName),
+ LH = local_file_header_from_info_method_name(FileInfo, UncompSize, CompMethod, FileName, GPFlag),
BLH = local_file_header_to_bin(LH),
B = [<<?LOCAL_FILE_MAGIC:32/little>>, BLH],
Out1 = Output({write, B}, Out0),
Out2 = Output({write, FileName}, Out1),
{Out3, CompSize, CRC} = put_z_file(CompMethod, UncompSize, Out2, F1,
0, Input, Output, OpO, Z, Type),
- FB(FileName),
+ FB(FileName0),
Patch = <<CRC:32/little, CompSize:32/little>>,
Out4 = Output({pwrite, Pos0 + ?LOCAL_FILE_HEADER_CRC32_OFFSET, Patch}, Out3),
Out5 = Output({seek, eof, 0}, Out4),
@@ -1012,7 +1030,7 @@ cd_file_header_from_lh_and_pos(LH, Pos) ->
uncomp_size = UncompSize,
file_name_length = FileNameLength,
extra_field_length = ExtraFieldLength} = LH,
- #cd_file_header{version_made_by = 20,
+ #cd_file_header{version_made_by = ?VERSION_MADE_BY,
version_needed = VersionNeeded,
gp_flag = GPFlag,
comp_method = CompMethod,
@@ -1026,7 +1044,7 @@ cd_file_header_from_lh_and_pos(LH, Pos) ->
file_comment_length = 0, % FileCommentLength,
disk_num_start = 0, % DiskNumStart,
internal_attr = 0, % InternalAttr,
- external_attr = 0, % ExternalAttr,
+ external_attr = ?CENTRAL_FILE_EXT_ATTRIBUTES, % ExternalAttr,
local_header_offset = Pos}.
cd_file_header_to_bin(
@@ -1103,10 +1121,10 @@ eocd_to_bin(#eocd{disk_num = DiskNum,
%% put together a local file header
local_file_header_from_info_method_name(#file_info{mtime = MTime},
UncompSize,
- CompMethod, Name) ->
+ CompMethod, Name, GPFlag) ->
{ModDate, ModTime} = dos_date_time_from_datetime(MTime),
#local_file_header{version_needed = 20,
- gp_flag = 0,
+ gp_flag = GPFlag,
comp_method = CompMethod,
last_mod_time = ModTime,
last_mod_date = ModDate,
@@ -1270,7 +1288,9 @@ get_central_dir(In0, RawIterator, Input) ->
In2 = Input({seek, bof, EOCD#eocd.offset}, In1),
N = EOCD#eocd.entries,
Acc0 = [],
- Out0 = RawIterator(EOCD, "", binary_to_list(BComment), <<>>, Acc0),
+ %% There is no encoding flag for the archive comment.
+ Comment = heuristic_to_string(BComment),
+ Out0 = RawIterator(EOCD, "", Comment, <<>>, Acc0),
get_cd_loop(N, In2, RawIterator, Input, Out0).
get_cd_loop(0, In, _RawIterator, _Input, Acc) ->
@@ -1286,20 +1306,32 @@ get_cd_loop(N, In0, RawIterator, Input, Acc0) ->
ExtraLen = CD#cd_file_header.extra_field_length,
CommentLen = CD#cd_file_header.file_comment_length,
ToRead = FileNameLen + ExtraLen + CommentLen,
+ GPFlag = CD#cd_file_header.gp_flag,
{B2, In2} = Input({read, ToRead}, In1),
{FileName, Comment, BExtra} =
- get_name_extra_comment(B2, FileNameLen, ExtraLen, CommentLen),
+ get_name_extra_comment(B2, FileNameLen, ExtraLen, CommentLen, GPFlag),
Acc1 = RawIterator(CD, FileName, Comment, BExtra, Acc0),
get_cd_loop(N-1, In2, RawIterator, Input, Acc1).
-get_name_extra_comment(B, FileNameLen, ExtraLen, CommentLen) ->
- case B of
- <<BFileName:FileNameLen/binary,
- BExtra:ExtraLen/binary,
- BComment:CommentLen/binary>> ->
- {binary_to_list(BFileName), binary_to_list(BComment), BExtra};
- _ ->
- throw(bad_central_directory)
+get_name_extra_comment(B, FileNameLen, ExtraLen, CommentLen, GPFlag) ->
+ try
+ <<BFileName:FileNameLen/binary,
+ BExtra:ExtraLen/binary,
+ BComment:CommentLen/binary>> = B,
+ {binary_to_chars(BFileName, GPFlag),
+ %% Appendix D says: "If general purpose bit 11 is unset, the
+ %% file name and comment should conform to the original ZIP
+ %% character encoding." However, it seems that at least Linux
+ %% zip(1) encodes the comment without setting bit 11 if the
+ %% filename is 7-bit ASCII. If bit 11 is set,
+ %% binary_to_chars/1 could (should?) be called (it can fail),
+ %% but the choice is to employ heuristics in this case too
+ %% (it does not fail).
+ heuristic_to_string(BComment),
+ BExtra}
+ catch
+ _:_ ->
+ throw(bad_central_directory)
end.
%% get end record, containing the offset to the central directory
@@ -1428,7 +1460,8 @@ get_z_file(In0, Z, Input, Output, OpO, FB,
LH#local_file_header.crc32}
end,
{BFileN, In3} = Input({read, FileNameLen + ExtraLen}, In1),
- {FileName, _} = get_file_name_extra(FileNameLen, ExtraLen, BFileN),
+ {FileName, _} =
+ get_file_name_extra(FileNameLen, ExtraLen, BFileN, GPFlag),
ReadAndWrite =
case check_valid_location(CWD, FileName) of
{true,FileName1} ->
@@ -1488,12 +1521,13 @@ check_dir_level([".." | Parts], Level) ->
check_dir_level([_Dir | Parts], Level) ->
check_dir_level(Parts, Level+1).
-get_file_name_extra(FileNameLen, ExtraLen, B) ->
- case B of
- <<BFileName:FileNameLen/binary, BExtra:ExtraLen/binary>> ->
- {binary_to_list(BFileName), BExtra};
- _ ->
- throw(bad_file_header)
+get_file_name_extra(FileNameLen, ExtraLen, B, GPFlag) ->
+ try
+ <<BFileName:FileNameLen/binary, BExtra:ExtraLen/binary>> = B,
+ {binary_to_chars(BFileName, GPFlag), BExtra}
+ catch
+ _:_ ->
+ throw(bad_file_header)
end.
%% get compressed or stored data
@@ -1597,6 +1631,38 @@ skip_bin(B, Pos) when is_binary(B) ->
_ -> <<>>
end.
+binary_to_chars(B, GPFlag) ->
+ ?SHOW_GP_BIT_11(B, GPFlag band ?GP_BIT_11),
+ case GPFlag band ?GP_BIT_11 of
+ 0 ->
+ binary_to_list(B);
+ ?GP_BIT_11 ->
+ case unicode:characters_to_list(B) of
+ List when is_list(List) ->
+ List
+ end
+ end.
+
+heuristic_to_string(B) when is_binary(B) ->
+ case unicode:characters_to_binary(B) of
+ B ->
+ unicode:characters_to_list(B);
+ _ ->
+ binary_to_list(B)
+ end.
+
+encode_string(String) ->
+ case lists:any(fun(C) -> C > 127 end, String) of
+ true ->
+ case unicode:characters_to_binary(String) of
+ B when is_binary(B) ->
+ {binary_to_list(B), ?GP_BIT_11};
+ _ ->
+ throw({bad_unicode, String})
+ end;
+ false ->
+ {String, 0}
+ end.
%% ZIP header manipulations
eocd_and_comment_from_bin(<<DiskNum:16/little,
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index 7f7a0834ba..4b923c5fe2 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -34,11 +34,6 @@ MODULES= \
escript_SUITE \
ets_SUITE \
ets_tough_SUITE \
- expand_test \
- expand_test1 \
- unicode_expand \
- ExpandTestCaps \
- ExpandTestCaps1 \
filelib_SUITE \
file_sorter_SUITE \
filename_SUITE \
@@ -79,6 +74,7 @@ MODULES= \
supervisor_deadlock \
naughty_child \
shell_SUITE \
+ shell_docs_SUITE \
supervisor_SUITE \
supervisor_bridge_SUITE \
sys_SUITE \
diff --git a/lib/stdlib/test/beam_lib_SUITE.erl b/lib/stdlib/test/beam_lib_SUITE.erl
index 9ef85a15e0..e28301ec9e 100644
--- a/lib/stdlib/test/beam_lib_SUITE.erl
+++ b/lib/stdlib/test/beam_lib_SUITE.erl
@@ -36,7 +36,8 @@
-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, strip_add_chunks/1, otp_6711/1,
- building/1, md5/1, encrypted_abstr/1, encrypted_abstr_file/1]).
+ building/1, md5/1, encrypted_abstr/1, encrypted_abstr_file/1,
+ missing_debug_info_backend/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -46,7 +47,7 @@ suite() ->
all() ->
[error, normal, cmp, cmp_literals, strip, strip_add_chunks, otp_6711,
- building, md5, encrypted_abstr, encrypted_abstr_file].
+ building, md5, encrypted_abstr, encrypted_abstr_file, missing_debug_info_backend].
groups() ->
[].
@@ -775,6 +776,29 @@ write_crypt_file(Contents0) ->
io:format("~s\n", [binary_to_list(Contents)]),
ok = file:write_file(".erlang.crypt", Contents).
+%% GH-4353: Don't crash when the backend for generating the abstract code
+%% is missing.
+missing_debug_info_backend(Conf) ->
+ PrivDir = ?privdir,
+ Simple = filename:join(PrivDir, "simple"),
+ Source = Simple ++ ".erl",
+ BeamFile = Simple ++ ".beam",
+ simple_file(Source),
+
+ %% Create a debug_info chunk with a non-existing backend.
+ {ok,simple} = compile:file(Source, [{outdir,PrivDir}]),
+ {ok,simple,All0} = beam_lib:all_chunks(BeamFile),
+ FakeBackend = definitely__not__an__existing__backend,
+ FakeDebugInfo = {debug_info_v1, FakeBackend, nothing_here},
+ All = lists:keyreplace("Dbgi", 1, All0, {"Dbgi", term_to_binary(FakeDebugInfo)}),
+ {ok,NewBeam} = beam_lib:build_module(All),
+ ok = file:write_file(BeamFile, NewBeam),
+
+ %% beam_lib should not crash, but return an error.
+ verify(missing_backend, beam_lib:chunks(BeamFile, [abstract_code])),
+
+ ok.
+
compare_chunks(File1, File2, ChunkIds) ->
{ok, {_, Chunks1}} = beam_lib:chunks(File1, ChunkIds),
{ok, {_, Chunks2}} = beam_lib:chunks(File2, ChunkIds),
diff --git a/lib/stdlib/test/dict_test_lib.erl b/lib/stdlib/test/dict_test_lib.erl
index 8f999f7cad..a85fd86ab9 100644
--- a/lib/stdlib/test/dict_test_lib.erl
+++ b/lib/stdlib/test/dict_test_lib.erl
@@ -39,6 +39,9 @@ new(Mod, Eq) ->
end.
empty(Mod) ->
+ %% The dict module might not be loaded since it not used by
+ %% anything in the core parts of Erlang/OTP.
+ {module,Mod} = code:ensure_loaded(Mod),
case erlang:function_exported(Mod, new, 0) of
false -> Mod:empty();
true -> Mod:new()
@@ -48,6 +51,7 @@ to_list(Mod, D) ->
Mod:to_list(D).
from_list(Mod, L) ->
+ {module,Mod} = code:ensure_loaded(Mod),
case erlang:function_exported(Mod, from_orddict, 1) of
false ->
Mod:from_list(L);
@@ -63,6 +67,7 @@ from_list(Mod, L) ->
%% Store new value into dictionary or update previous value in dictionary.
enter(Mod, Key, Val, Dict) ->
+ {module,Mod} = code:ensure_loaded(Mod),
case erlang:function_exported(Mod, store, 3) of
false ->
Mod:enter(Key, Val, Dict);
diff --git a/lib/stdlib/test/digraph_SUITE.erl b/lib/stdlib/test/digraph_SUITE.erl
index b5d3452616..ce0bc90f1c 100644
--- a/lib/stdlib/test/digraph_SUITE.erl
+++ b/lib/stdlib/test/digraph_SUITE.erl
@@ -31,7 +31,7 @@
init_per_group/2,end_per_group/2]).
-export([opts/1, degree/1, path/1, cycle/1, vertices/1,
- edges/1, data/1, otp_3522/1, otp_3630/1, otp_8066/1]).
+ edges/1, data/1, otp_3522/1, otp_3630/1, otp_8066/1, vertex_names/1]).
-export([spawn_graph/2]).
@@ -41,10 +41,10 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[opts, degree, path, cycle, {group, misc},
- {group, tickets}].
+ {group, tickets}, vertex_names].
groups() ->
- [{misc, [], [vertices, edges, data]},
+ [{misc, [], [vertices, edges, data, vertex_names]},
{tickets, [], [otp_3522, otp_3630, otp_8066]}].
init_per_suite(Config) ->
@@ -337,6 +337,51 @@ otp_8066(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+vertex_names(Config) when is_list(Config) ->
+ %% Check that a node named '_' does not lead to wildcard matches
+ %% in ets.
+
+ G = digraph:new([acyclic]),
+ A = digraph:add_vertex(G, 'A'),
+ B = digraph:add_vertex(G, '_'),
+ AB = digraph:add_edge(G, A, B),
+
+ %% Link A -> B
+ 1 = digraph:out_degree(G, A),
+ 1 = digraph:in_degree(G, B),
+ 0 = digraph:out_degree(G, B),
+ 0 = digraph:in_degree(G, A),
+ [B] = digraph:out_neighbours(G, A),
+ [A] = digraph:in_neighbours(G, B),
+ [] = digraph:out_neighbours(G, B),
+ [] = digraph:in_neighbours(G, A),
+ [AB] = digraph:out_edges(G, A),
+ [AB] = digraph:in_edges(G, B),
+ [] = digraph:out_edges(G, B),
+ [] = digraph:in_edges(G, A),
+
+ %% Reverse the edge
+ digraph:del_edge(G, AB),
+ BA = digraph:add_edge(G, B, A),
+
+ 1 = digraph:out_degree(G, B),
+ 1 = digraph:in_degree(G, A),
+ 0 = digraph:out_degree(G, A),
+ 0 = digraph:in_degree(G, B),
+ [A] = digraph:out_neighbours(G, B),
+ [B] = digraph:in_neighbours(G, A),
+ [] = digraph:out_neighbours(G, A),
+ [] = digraph:in_neighbours(G, B),
+ [BA] = digraph:out_edges(G, B),
+ [BA] = digraph:in_edges(G, A),
+ [] = digraph:out_edges(G, A),
+ [] = digraph:in_edges(G, B),
+
+ digraph:delete(G),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
sane(G) ->
sane1(G),
erase(sane) =:= undefined.
diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl
index 5c2b1965ba..dd6b25a531 100644
--- a/lib/stdlib/test/edlin_expand_SUITE.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE.erl
@@ -27,6 +27,7 @@
-include_lib("common_test/include/ct.hrl").
init_per_testcase(_Case, Config) ->
+ cleanup(),
Config.
end_per_testcase(_Case, _Config) ->
@@ -44,10 +45,6 @@ groups() ->
[].
init_per_suite(Config) ->
- (catch code:delete(expand_test)),
- (catch code:delete(expand_test1)),
- (catch code:delete('ExpandTestCaps')),
- (catch code:delete('ExpandTestCaps1')),
Config.
end_per_suite(_Config) ->
@@ -59,9 +56,15 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+cleanup() ->
+ [try
+ code:purge(M),
+ code:delete(M)
+ catch _:_ -> ok end || M <- [expand_test, expand_test1,
+ 'ExpandTestCaps', 'ExpandTestCaps2']].
normal(Config) when is_list(Config) ->
- {module,expand_test} = c:l(expand_test),
+ {module,expand_test} = compile_and_load(Config,expand_test),
%% These tests might fail if another module with the prefix
%% "expand_" happens to also be loaded.
{yes, "test:", []} = do_expand("expand_"),
@@ -80,8 +83,8 @@ normal(Config) when is_list(Config) ->
%% Normal module name, some function names using quoted atoms.
quoted_fun(Config) when is_list(Config) ->
- {module,expand_test} = c:l(expand_test),
- {module,expand_test1} = c:l(expand_test1),
+ {module,expand_test} = compile_and_load(Config,expand_test),
+ {module,expand_test1} = compile_and_load(Config,expand_test1),
%% should be no colon after test this time
{yes, "test", []} = do_expand("expand_"),
{no, [], []} = do_expand("expandXX_"),
@@ -112,7 +115,7 @@ quoted_fun(Config) when is_list(Config) ->
ok.
quoted_module(Config) when is_list(Config) ->
- {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
+ {module,'ExpandTestCaps'} = compile_and_load(Config,'ExpandTestCaps'),
{yes, "Caps':", []} = do_expand("'ExpandTest"),
{no,[],
[{"a_fun_name",1},
@@ -125,8 +128,8 @@ quoted_module(Config) when is_list(Config) ->
ok.
quoted_both(Config) when is_list(Config) ->
- {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
- {module,'ExpandTestCaps1'} = c:l('ExpandTestCaps1'),
+ {module,'ExpandTestCaps'} = compile_and_load(Config,'ExpandTestCaps'),
+ {module,'ExpandTestCaps1'} = compile_and_load(Config,'ExpandTestCaps1'),
%% should be no colon (or quote) after test this time
{yes, "Caps", []} = do_expand("'ExpandTest"),
{no,[],[{"'#weird-fun-name'",0},
@@ -229,7 +232,7 @@ check_trailing([I|Str], ArityStr, Suffix, Dots) ->
end.
unicode(Config) when is_list(Config) ->
- {module,unicode_expand} = c:l('unicode_expand'),
+ {module,unicode_expand} = compile_and_load(Config,'unicode_expand'),
{no,[],[{"'кlирилли́ческий атом'",0},
{"'кlирилли́ческий атом'",1},
{"'кlирилли́ческий атомB'",1},
@@ -253,3 +256,10 @@ do_expand(String) ->
do_format(StringList) ->
lists:flatten(edlin_expand:format_matches(StringList)).
+
+compile_and_load(Config,Module) ->
+ Filename = filename:join(
+ proplists:get_value(data_dir,Config),
+ atom_to_list(Module)),
+ {ok,Module,Bin} = compile:file(Filename, [binary]),
+ code:load_binary(Module, Filename, Bin).
diff --git a/lib/stdlib/test/ExpandTestCaps.erl b/lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps.erl
index 7fb1107ae0..7fb1107ae0 100644
--- a/lib/stdlib/test/ExpandTestCaps.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps.erl
diff --git a/lib/stdlib/test/ExpandTestCaps1.erl b/lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps1.erl
index 400a17b137..400a17b137 100644
--- a/lib/stdlib/test/ExpandTestCaps1.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/ExpandTestCaps1.erl
diff --git a/lib/stdlib/test/expand_test.erl b/lib/stdlib/test/edlin_expand_SUITE_data/expand_test.erl
index 5544923a12..5544923a12 100644
--- a/lib/stdlib/test/expand_test.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/expand_test.erl
diff --git a/lib/stdlib/test/expand_test1.erl b/lib/stdlib/test/edlin_expand_SUITE_data/expand_test1.erl
index abefaefcfd..abefaefcfd 100644
--- a/lib/stdlib/test/expand_test1.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/expand_test1.erl
diff --git a/lib/stdlib/test/unicode_expand.erl b/lib/stdlib/test/edlin_expand_SUITE_data/unicode_expand.erl
index 41f741fa84..41f741fa84 100644
--- a/lib/stdlib/test/unicode_expand.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE_data/unicode_expand.erl
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index a90beed4f3..a607598136 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -29,7 +29,7 @@
otp_8562/1, otp_8665/1, otp_8911/1, otp_10302/1, otp_10820/1,
otp_11728/1, encoding/1, extends/1, function_macro/1,
test_error/1, test_warning/1, otp_14285/1,
- test_if/1,source_name/1]).
+ test_if/1,source_name/1,otp_16978/1]).
-export([epp_parse_erl_form/2]).
@@ -70,7 +70,7 @@ all() ->
overload_mac, otp_8388, otp_8470, otp_8562,
otp_8665, otp_8911, otp_10302, otp_10820, otp_11728,
encoding, extends, function_macro, test_error, test_warning,
- otp_14285, test_if, source_name].
+ otp_14285, test_if, source_name, otp_16978].
groups() ->
[{upcase_mac, [], [upcase_mac_1, upcase_mac_2]},
@@ -1154,7 +1154,12 @@ test_if(Config) ->
{if_7c,
<<"-if(begin true end).\n" %Not a guard expression.
"-endif.\n">>,
- {errors,[{1,epp,{bad,'if'}}],[]}}
+ {errors,[{1,epp,{bad,'if'}}],[]}},
+
+ {if_8c,
+ <<"-if(?foo).\n" %Undefined symbol.
+ "-endif.\n">>,
+ {errors,[{1,epp,{undefined,foo,none}}],[]}}
],
[] = compile(Config, Cs),
@@ -1715,21 +1720,48 @@ source_name_1(File, Expected) ->
Res = epp:parse_file(File, [{source_name, Expected}]),
{ok, [{attribute,_,file,{Expected,_}} | _Forms]} = Res.
+otp_16978(Config) when is_list(Config) ->
+ %% A test of erl_parse:tokens().
+ P = <<"t() -> ?a.">>,
+ Vs = [#{},
+ #{k => 1,[[a],[{}]] => "str"},
+ #{#{} => [{#{x=>#{3=>$3}}},{3.14,#{}}]}],
+ Ts = [{erl_parse_tokens,
+ P,
+ [{d,{a,V}}],
+ V} || V <- Vs],
+ [] = run(Config, Ts),
+
+ ok.
+
check(Config, Tests) ->
- eval_tests(Config, fun check_test/2, Tests).
+ eval_tests(Config, fun check_test/3, Tests).
compile(Config, Tests) ->
- eval_tests(Config, fun compile_test/2, Tests).
+ eval_tests(Config, fun compile_test/3, Tests).
run(Config, Tests) ->
- eval_tests(Config, fun run_test/2, Tests).
+ eval_tests(Config, fun run_test/3, Tests).
eval_tests(Config, Fun, Tests) ->
- F = fun({N,P,E}, BadL) ->
+ TestsWithOpts =
+ [case Test of
+ {N,P,E} ->
+ {N,P,[],E};
+ {_,_,_,_} ->
+ Test
+ end || Test <- Tests],
+ F = fun({N,P,Opts,E}, BadL) ->
%% io:format("Testing ~p~n", [P]),
- Return = Fun(Config, P),
+ Return = Fun(Config, P, Opts),
case message_compare(E, Return) of
true ->
+ case E of
+ {errors, Errors} ->
+ call_format_error(Errors);
+ _ ->
+ ok
+ end,
BadL;
false ->
io:format("~nTest ~p failed. Expected~n ~p~n"
@@ -1737,15 +1769,14 @@ eval_tests(Config, Fun, Tests) ->
fail()
end
end,
- lists:foldl(F, [], Tests).
-
+ lists:foldl(F, [], TestsWithOpts).
-check_test(Config, Test) ->
+check_test(Config, Test, Opts) ->
Filename = "epp_test.erl",
PrivDir = proplists:get_value(priv_dir, Config),
File = filename:join(PrivDir, Filename),
ok = file:write_file(File, Test),
- case epp:parse_file(File, [PrivDir], []) of
+ case epp:parse_file(File, [PrivDir], Opts) of
{ok,Forms} ->
Errors = [E || E={error,_} <- Forms],
call_format_error([E || {error,E} <- Errors]),
@@ -1754,15 +1785,18 @@ check_test(Config, Test) ->
Error
end.
-compile_test(Config, Test0) ->
+compile_test(Config, Test0, Opts0) ->
Test = [<<"-module(epp_test). ">>, Test0],
Filename = "epp_test.erl",
PrivDir = proplists:get_value(priv_dir, Config),
File = filename:join(PrivDir, Filename),
ok = file:write_file(File, Test),
- Opts = [export_all,nowarn_export_all,return,nowarn_unused_record,{outdir,PrivDir}],
+ Opts = [export_all,nowarn_export_all,return,nowarn_unused_record,{outdir,PrivDir}] ++ Opts0,
case compile_file(File, Opts) of
{ok, Ws} -> warnings(File, Ws);
+ {errors, Errors}=Else ->
+ call_format_error(Errors),
+ Else;
Else -> Else
end.
@@ -1808,13 +1842,13 @@ epp_parse_file(File, Opts) ->
unopaque_forms(Forms) ->
[erl_parse:anno_to_term(Form) || Form <- Forms].
-run_test(Config, Test0) ->
+run_test(Config, Test0, Opts0) ->
Test = [<<"-module(epp_test). -export([t/0]). ">>, Test0],
Filename = "epp_test.erl",
PrivDir = proplists:get_value(priv_dir, Config),
File = filename:join(PrivDir, Filename),
ok = file:write_file(File, Test),
- Opts = [return, {i,PrivDir},{outdir,PrivDir}],
+ Opts = [return, {i,PrivDir},{outdir,PrivDir}] ++ Opts0,
{ok, epp_test, []} = compile:file(File, Opts),
AbsFile = filename:rootname(File, ".erl"),
{module, epp_test} = code:load_abs(AbsFile, epp_test),
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index c7556f6f7e..a29b60cd1d 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -49,7 +49,9 @@
eep37/1,
eep43/1,
otp_15035/1,
- otp_16439/1]).
+ otp_16439/1,
+ otp_14708/1,
+ otp_16545/1]).
%%
%% Define to run outside of test server
@@ -89,7 +91,7 @@ all() ->
otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
otp_8133, otp_10622, otp_13228, otp_14826,
funs, try_catch, eval_expr_5, zero_width,
- eep37, eep43, otp_15035, otp_16439].
+ eep37, eep43, otp_15035, otp_16439, otp_14708, otp_16545].
groups() ->
[].
@@ -1674,10 +1676,75 @@ otp_16439(Config) when is_list(Config) ->
"case 7 of - - 7 -> seven end.", seven),
{ok,Ts,_} = erl_scan:string("- #{}. "),
- {ok,[{op,1,'-',{map,1,[]}}]} = erl_parse:parse_exprs(Ts),
+ A = erl_anno:new(1),
+ {ok,[{op,A,'-',{map,A,[]}}]} = erl_parse:parse_exprs(Ts),
ok.
+%% Test guard expressions in keys for maps and in sizes in binary matching.
+
+otp_14708(Config) when is_list(Config) ->
+ check(fun() -> X = 42, #{{tag,X} := V} = #{{tag,X} => a}, V end,
+ "begin X = 42, #{{tag,X} := V} = #{{tag,X} => a}, V end.",
+ a),
+ check(fun() ->
+ T = {x,y,z},
+ Map = #{x => 99, y => 100},
+ #{element(1, T) := V1, element(2, T) := V2} = Map,
+ {V1, V2}
+ end,
+ "begin
+ T = {x,y,z},
+ Map = #{x => 99, y => 100},
+ #{element(1, T) := V1, element(2, T) := V2} = Map,
+ {V1, V2}
+ end.",
+ {99, 100}),
+ error_check("#{term_to_binary(42) := _} = #{}.", illegal_guard_expr),
+
+ check(fun() ->
+ <<Sz:16,Body:(Sz-1)/binary>> = <<4:16,1,2,3>>,
+ Body
+ end,
+ "begin
+ <<Sz:16,Body:(Sz-1)/binary>> = <<4:16,1,2,3>>,
+ Body
+ end.",
+ <<1,2,3>>),
+ check(fun() ->
+ Sizes = #{0 => 3, 1 => 7},
+ <<SzTag:1,Body:(map_get(SzTag, Sizes))/binary>> =
+ <<1:1,1,2,3,4,5,6,7>>,
+ Body
+ end,
+ "begin
+ Sizes = #{0 => 3, 1 => 7},
+ <<SzTag:1,Body:(map_get(SzTag, Sizes))/binary>> =
+ <<1:1,1,2,3,4,5,6,7>>,
+ Body
+ end.",
+ <<1,2,3,4,5,6,7>>),
+ error_check("<<X:(process_info(self()))>> = <<>>.", illegal_bitsize),
+
+ ok.
+
+otp_16545(Config) when is_list(Config) ->
+ case eval_string("<<$W/utf16-native>> = <<$W/utf16-native>>.") of
+ <<$W/utf16-native>> -> ok
+ end,
+ case eval_string("<<$W/utf32-native>> = <<$W/utf32-native>>.") of
+ <<$W/utf32-native>> -> ok
+ end,
+ check(fun() -> <<10/unsigned,"fgbz":86>> end,
+ "<<10/unsigned,\"fgbz\":86>>.",
+ <<10,0,0,0,0,0,0,0,0,0,1,152,0,0,0,0,0,0,0,0,0,6,112,0,0,
+ 0,0,0,0,0,0,0,24,128,0,0,0,0,0,0,0,0,0,122>>),
+ check(fun() -> <<"":16/signed>> end,
+ "<<\"\":16/signed>>.",
+ <<>>),
+ error_check("<<\"\":problem/signed>>.", badarg),
+ 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_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl
index 3885f6648d..257546d6a4 100644
--- a/lib/stdlib/test/erl_expand_records_SUITE.erl
+++ b/lib/stdlib/test/erl_expand_records_SUITE.erl
@@ -702,9 +702,13 @@ otp_7078(Config) when is_list(Config) ->
-record(otp_7101, {a,b,c=[],d=[],e=[]}).
+id(I) -> I.
+
%% OTP-7101. Record update: more than one call to setelement/3.
otp_7101(Config) when is_list(Config) ->
- Rec = #otp_7101{},
+ %% Ensure the compiler won't do any funny constant propagation tricks.
+ id(#otp_7101{a=a,b=b,c=c,d=d,e=e}),
+ Rec = id(#otp_7101{}),
%% Spawn a tracer process to count the number of setelement/3 calls.
%% The tracer will forward all trace messages to us.
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 38d07249fd..aa941e61b6 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-2019. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -69,7 +69,8 @@
stacktrace_syntax/1,
otp_14285/1, otp_14378/1,
external_funs/1,otp_15456/1,otp_15563/1,
- unused_type/1]).
+ unused_type/1,removed/1, otp_16516/1,
+ inline_nifs/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -91,7 +92,8 @@ all() ->
otp_11851, otp_11879, otp_13230,
record_errors, otp_11879_cont, non_latin1_module, otp_14323,
stacktrace_syntax, otp_14285, otp_14378, external_funs,
- otp_15456, otp_15563, unused_type].
+ otp_15456, otp_15563, unused_type, removed, otp_16516,
+ inline_nifs].
groups() ->
[{unused_vars_warn, [],
@@ -469,7 +471,7 @@ unused_vars_warn_lc(Config) when is_list(Config) ->
">>,
[warn_unused_vars],
{warnings,[{6,erl_lint,{unused_var,'C1'}},
- {7,sys_core_fold,no_clause_match},
+ {7,sys_core_fold,no_clause_match},
{9,erl_lint,{unused_var,'C3'}}]}},
{lc21,
@@ -1298,6 +1300,10 @@ unsized_binary_in_bin_gen_pattern(Config) when is_list(Config) ->
<< <<X,Tail/bits>> || <<X,Tail/bits>> <= Bin >>;
t({bc,bitstring,Bin}) ->
<< <<X,Tail/bits>> || <<X,Tail/bitstring>> <= Bin >>;
+ t({bc,binary_lit,Bin}) ->
+ << <<X>> || <<X,1/binary>> <= Bin >>;
+ t({bc,bytes_lit,Bin}) ->
+ << <<X>> || <<X,a/bytes>> <= Bin >>;
t({lc,binary,Bin}) ->
[ {X,Tail} || <<X,Tail/binary>> <= Bin ];
t({lc,bytes,Bin}) ->
@@ -1305,17 +1311,25 @@ unsized_binary_in_bin_gen_pattern(Config) when is_list(Config) ->
t({lc,bits,Bin}) ->
[ {X,Tail} || <<X,Tail/bits>> <= Bin ];
t({lc,bitstring,Bin}) ->
- [ {X,Tail} || <<X,Tail/bitstring>> <= Bin ].">>,
+ [ {X,Tail} || <<X,Tail/bitstring>> <= Bin ];
+ t({lc,bits_lit,Bin}) ->
+ [ X || <<X,42/bits>> <= Bin ];
+ t({lc,bitstring_lit,Bin}) ->
+ [ X || <<X,b/bitstring>> <= Bin ].">>,
[],
{errors,
[{2,erl_lint,unsized_binary_in_bin_gen_pattern},
{4,erl_lint,unsized_binary_in_bin_gen_pattern},
{6,erl_lint,unsized_binary_in_bin_gen_pattern},
{8,erl_lint,unsized_binary_in_bin_gen_pattern},
- {10,erl_lint,unsized_binary_in_bin_gen_pattern},
- {12,erl_lint,unsized_binary_in_bin_gen_pattern},
- {14,erl_lint,unsized_binary_in_bin_gen_pattern},
- {16,erl_lint,unsized_binary_in_bin_gen_pattern}],
+ {10,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {12,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {14,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {16,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {18,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {20,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {22,erl_lint,unsized_binary_in_bin_gen_pattern},
+ {24,erl_lint,unsized_binary_in_bin_gen_pattern}],
[]}}],
[] = run(Config, Ts),
ok.
@@ -2061,8 +2075,9 @@ otp_5362(Config) when is_list(Config) ->
{error,
[{5,erl_lint,{call_to_redefined_old_bif,{spawn,1}}}],
[{4,erl_lint,{deprecated,{erlang,now,0},
- "Deprecated BIF. See the \"Time and Time Correction in Erlang\" "
- "chapter of the ERTS User's Guide for more information."}}]}},
+ "see the \"Time and Time Correction in Erlang\" "
+ "chapter of the ERTS User's Guide for more "
+ "information"}}]}},
{otp_5362_5,
<<"-compile(nowarn_deprecated_function).
-compile(nowarn_bif_clash).
@@ -2119,8 +2134,9 @@ otp_5362(Config) when is_list(Config) ->
{nowarn_bif_clash,{spawn,1}}]}, % has no effect
{warnings,
[{5,erl_lint,{deprecated,{erlang,now,0},
- "Deprecated BIF. See the \"Time and Time Correction in Erlang\" "
- "chapter of the ERTS User's Guide for more information."}}]}},
+ "see the \"Time and Time Correction in Erlang\" "
+ "chapter of the ERTS User's Guide for more "
+ "information"}}]}},
{otp_5362_9,
<<"-include_lib(\"stdlib/include/qlc.hrl\").
@@ -2150,13 +2166,15 @@ otp_5362(Config) when is_list(Config) ->
[],
{warnings,
[{1,erl_lint,{deprecated,{calendar,local_time_to_universal_time,1},
- {calendar,local_time_to_universal_time_dst,1}, "a future release"}}]}},
+ "use calendar:local_time_to_universal_time_dst/1 "
+ "instead"}}]}},
{call_removed_function,
<<"t(X) -> erlang:hash(X, 10000).">>,
[],
{warnings,
- [{1,erl_lint,{removed,{erlang,hash,2},{erlang,phash2,2},"20.0"}}]}},
+ [{1,erl_lint,{removed,{erlang,hash,2},
+ "use erlang:phash2/2 instead"}}]}},
{nowarn_call_removed_function_1,
<<"t(X) -> erlang:hash(X, 10000).">>,
@@ -2173,7 +2191,7 @@ otp_5362(Config) when is_list(Config) ->
[],
{warnings,[{1,erl_lint,
{removed,{os_mon_mib,any_function_really,1},
- "was removed in 22.0"}}]}},
+ "this module was removed in OTP 22.0"}}]}},
{nowarn_call_removed_module,
<<"t(X) -> os_mon_mib:any_function_really(X).">>,
@@ -2212,7 +2230,6 @@ otp_15456(Config) when is_list(Config) ->
warn_deprecated_function]},
{warnings,[{5,erl_lint,
{deprecated,{random,seed,3},
- "the 'random' module is deprecated; "
"use the 'rand' module instead"}}]}},
%% {nowarn_unused_function,[{M,F,A}]} can be given
@@ -3645,6 +3662,7 @@ bin_syntax_errors(Config) ->
Ts = [{bin_syntax_errors,
<<"t(<<X:bad_size>>) -> X;
t(<<_:(x ! y)/integer>>) -> ok;
+ t(<<_:(l())/integer>>) -> ok;
t(<<X:all/integer>>) -> X;
t(<<X/bad_type>>) -> X;
t(<<X/unit:8>>) -> X;
@@ -3653,22 +3671,30 @@ bin_syntax_errors(Config) ->
t(<<(x ! y):8/integer>>) -> ok;
t(X) ->
{<<X/binary-integer>>,<<X/signed-unsigned-integer>>,
- <<X/little-big>>,<<X/unit:4-unit:8>>}.
+ <<X/little-big>>,<<X/unit:4-unit:8>>};
+ t(<<_:{A,B}>>) -> ok.
+
+ l() ->
+ foo.
">>,
[],
- {error,[{1,erl_lint,illegal_bitsize},
- {2,erl_lint,illegal_bitsize},
- {3,erl_lint,illegal_bitsize},
- {4,erl_lint,{undefined_bittype,bad_type}},
- {5,erl_lint,bittype_unit},
- {7,erl_lint,illegal_pattern},
+ {error,[{2,erl_lint,illegal_bitsize},
+ {3,erl_lint,{illegal_bitsize_local_call,{l,0}}},
+ {5,erl_lint,{undefined_bittype,bad_type}},
+ {6,erl_lint,bittype_unit},
{8,erl_lint,illegal_pattern},
- {10,erl_lint,{bittype_mismatch,integer,binary,"type"}},
- {10,erl_lint,{bittype_mismatch,unsigned,signed,"sign"}},
- {11,erl_lint,{bittype_mismatch,8,4,"unit"}},
- {11,erl_lint,{bittype_mismatch,big,little,"endianness"}}
+ {9,erl_lint,illegal_pattern},
+ {11,erl_lint,{bittype_mismatch,integer,binary,"type"}},
+ {11,erl_lint,{bittype_mismatch,unsigned,signed,"sign"}},
+ {12,erl_lint,{bittype_mismatch,8,4,"unit"}},
+ {12,erl_lint,{bittype_mismatch,big,little,"endianness"}},
+ {13,erl_lint,{unbound_var,'A'}},
+ {13,erl_lint,{unbound_var,'B'}}
],
- [{6,erl_lint,{bad_bitsize,"float"}}]}}
+ [{1,erl_lint,non_integer_bitsize},
+ {4,erl_lint,non_integer_bitsize},
+ {7,erl_lint,{bad_bitsize,"float"}},
+ {13,erl_lint,non_integer_bitsize}]}}
],
[] = run(Config, Ts),
ok.
@@ -3779,7 +3805,7 @@ maps(Config) ->
">>,
[],
{errors,[{4,erl_lint,illegal_map_construction},
- {6,erl_lint,illegal_map_key}],[]}},
+ {6,erl_lint,{unbound_var,'V'}}],[]}},
{unused_vars_with_empty_maps,
<<"t(Foo, Bar, Baz) -> {#{},#{}}.">>,
[warn_unused_variables],
@@ -4128,9 +4154,9 @@ otp_14378(Config) ->
[],
{warnings,[{4,erl_lint,
{deprecated,{erlang,now,0},
- "Deprecated BIF. See the \"Time and Time Correction"
- " in Erlang\" chapter of the ERTS User's Guide"
- " for more information."}}]}}],
+ "see the \"Time and Time Correction in Erlang\" "
+ "chapter of the ERTS User's Guide for more "
+ "information"}}]}}],
[] = run(Config, Ts),
ok.
@@ -4291,6 +4317,140 @@ otp_15563(Config) when is_list(Config) ->
[] = run(Config, Ts),
ok.
+removed(Config) when is_list(Config) ->
+ Ts = [{removed,
+ <<"-removed([{nonexistent,1,\"hi\"}]). %% okay since it doesn't exist
+ -removed([frutt/0]). %% okay since frutt/0 is not exported
+ -removed([t/0]). %% not okay since t/0 is exported
+ -removed([{t,'_'}]). %% not okay since t/0 is exported
+ -removed([{'_','_'}]). %% not okay since t/0 is exported
+ -removed([{{badly,formed},1}]).
+ -removed('badly formed').
+ -export([t/0]).
+ frutt() -> ok.
+ t() -> ok.
+ ">>,
+ {[]},
+ {error,[{3,erl_lint,{bad_removed,{t,0}}},
+ {4,erl_lint,{bad_removed,{t,'_'}}},
+ {5,erl_lint,{bad_removed,{'_','_'}}},
+ {6,erl_lint,{invalid_removed,{{badly,formed},1}}},
+ {7,erl_lint,{invalid_removed,'badly formed'}}],
+ [{9,erl_lint,{unused_function,{frutt,0}}}]}}
+ ],
+ [] = run(Config, Ts),
+ ok.
+
+otp_16516(Config) when is_list(Config) ->
+ one_multi_init(Config),
+ several_multi_inits(Config).
+
+one_multi_init(Config) ->
+ "'_' initializes no omitted fields" =
+ format_error(bad_multi_field_init),
+ Ts = [{otp_16516_1,
+ <<"-record(r, {f}).
+ t(#r{f = 17, _ = V}) ->
+ V.
+ u(#r{_ = V, f = 17}) ->
+ V.
+ -record(r1, {f, g = 17}).
+ g(#r1{f = 3, _ = 42}) ->
+ g.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,bad_multi_field_init},
+ {4,erl_lint,bad_multi_field_init}],[]}},
+ {otp_16516_2,
+ %% No error since "_ = '_'" is actually used as a catch-all
+ %% initialization. V is unused (as compilation with the 'E'
+ %% option shows), but no warning about V being unused is
+ %% output.
+ <<"-record(r, {f}).
+ t(V) ->
+ #r{f = 3, _ = V}.
+ u(V) ->
+ #r{_ = V, f = 3}.
+ ">>,
+ [],
+ []},
+ {otp_16516_3,
+ %% No error in this case either. And no unused variable warning.
+ <<"-record(r, {f}).
+ t(V) when #r{f = 3, _ = V} =:= #r{f = 3} ->
+ a.
+ u(V) when #r{_ = V, f = 3} =:= #r{f = 3} ->
+ a.
+ ">>,
+ [],
+ []}],
+ [] = run(Config, Ts).
+
+several_multi_inits(Config) ->
+ Ts = [{otp_16516_4,
+ <<"-record(r, {f, g}).
+ t(#r{f = 17, _ = V1,
+ _ = V2}) ->
+ {V1, V2}.
+ u(#r{_ = V1, f = 17,
+ _ = V2}) ->
+ {V1, V2}.
+ v(#r{_ = V1, f = 17}) ->
+ V1.
+ ">>,
+ [],
+ {errors,[{3,erl_lint,bad_multi_field_init},
+ {4,erl_lint,{unbound_var,'V1'}},
+ {4,erl_lint,{unbound_var,'V2'}},
+ {6,erl_lint,bad_multi_field_init},
+ {7,erl_lint,{unbound_var,'V1'}},
+ {7,erl_lint,{unbound_var,'V2'}}],[]}},
+ {otp_16516_5,
+ <<"-record(r, {f, g}).
+ t(V1, V2) ->
+ #r{_ = V1, f = 3,
+ _ = V2}.
+ u(V1, V2) ->
+ #r{_ = V1,
+ _ = V2, f = 3}.
+ ">>,
+ [],
+ {error,[{4,erl_lint,bad_multi_field_init},
+ {7,erl_lint,bad_multi_field_init}],
+ [{2,erl_lint,{unused_var,'V2'}},{5,erl_lint,{unused_var,'V2'}}]}},
+ {otp_16516_6,
+ <<"-record(r, {f, g}).
+ t(V1, V2) when #r{_ = V1, f = 3,
+ _ = V2} =:= #r{} ->
+ a.
+ u(V1, V2) when #r{_ = V1, f = 3,
+ _ = V2} =:= #r{} ->
+ a.
+ ">>,
+ [],
+ {error,[{3,erl_lint,bad_multi_field_init},
+ {6,erl_lint,bad_multi_field_init}],
+ [{2,erl_lint,{unused_var,'V2'}},
+ {5,erl_lint,{unused_var,'V2'}}]}}],
+ [] = run(Config, Ts).
+
+inline_nifs(Config) ->
+ Ts = [{implicit_inline,
+ <<"-compile(inline).
+ t() -> erlang:load_nif([], []).
+ gurka() -> ok.
+ ">>,
+ [],
+ {warnings,[{2,erl_lint,nif_inline}]}},
+ {explicit_inline,
+ <<"-compile({inline, [gurka/0]}).
+ t() -> erlang:load_nif([], []).
+ gurka() -> ok.
+ ">>,
+ [],
+ {warnings,[{2,erl_lint,nif_inline}]}}],
+ [] = run(Config, Ts).
+
format_error(E) ->
lists:flatten(erl_lint:format_error(E)).
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 48152243f8..4edecdb8c3 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -47,6 +47,7 @@
hook/1,
neg_indent/1,
maps_syntax/1,
+ format_options/1,
quoted_atom_types/1,
otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
@@ -76,7 +77,8 @@ groups() ->
[{expr, [],
[func, call, recs, try_catch, if_then, receive_after,
bits, head_tail, cond1, block, case1, ops,
- messages, maps_syntax, quoted_atom_types
+ messages, maps_syntax, quoted_atom_types,
+ format_options
]},
{attributes, [], [misc_attrs, import_export, dialyzer_attrs]},
{tickets, [],
@@ -545,6 +547,36 @@ import_export(Config) when is_list(Config) ->
compile(Config, Ts),
ok.
+format_options(Config) when is_list(Config) ->
+ "case 1 of\n"
+ " 2 ->\n"
+ " 3;\n"
+ " 4 ->\n"
+ " 5\n"
+ "end" = flat_parse_and_pp_expr("case 1 of 2 -> 3; 4 -> 5 end", 0, [{indent, 2}]),
+
+ "-spec foo(bar(),\n"
+ " qux()) ->\n"
+ " T |\n"
+ " baz(T)\n"
+ " when\n"
+ " T ::\n"
+ " tuple().\n" =
+ lists:flatten(
+ parse_and_pp_forms(
+ "-spec foo(bar(), qux()) -> T | baz(T) when T :: tuple().",
+ [{indent, 2}, {linewidth, 20}]
+ )
+ ),
+
+ "-spec foo(bar(), qux()) -> T | baz(T) when T :: tuple().\n" =
+ lists:flatten(
+ parse_and_pp_forms(
+ "-spec foo(bar(), qux()) -> T | baz(T) when T :: tuple().",
+ [{indent, 2}, {linewidth, 1000}]
+ )
+ ).
+
misc_attrs(Config) when is_list(Config) ->
ok = pp_forms(<<"-module(m). ">>),
ok = pp_forms(<<"-module(m, [Aafjlksfjdlsjflsdfjlsdjflkdsfjlk,"
@@ -1273,7 +1305,7 @@ otp_16435(_Config) ->
CheckF("f() ->\n << \n (catch <<1:4>>) ||\n"
" A <- []\n >>.\n"),
CheckF("f() ->\n [ \n (catch foo) ||\n A <- []\n ].\n"),
-
+ CheckF("f() when erlang:float(3.0) ->\n true.\n"),
Check = fun(S) -> S = flat_parse_and_pp_expr(S, 0, []) end,
Check("5 #r4.f1"),
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index aca5b1e54f..162e2a0a3d 100644
--- a/lib/stdlib/test/erl_scan_SUITE.erl
+++ b/lib/stdlib/test/erl_scan_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@
init_per_group/2,end_per_group/2]).
-export([error_1/1, error_2/1, iso88591/1, otp_7810/1, otp_10302/1,
- otp_10990/1, otp_10992/1, otp_11807/1]).
+ otp_10990/1, otp_10992/1, otp_11807/1, otp_16480/1]).
-import(lists, [nth/2,flatten/1]).
-import(io_lib, [print/1]).
@@ -58,7 +58,7 @@ suite() ->
all() ->
[{group, error}, iso88591, otp_7810, otp_10302, otp_10990, otp_10992,
- otp_11807].
+ otp_11807, otp_16480].
groups() ->
[{error, [], [error_1, error_2]}].
@@ -300,6 +300,32 @@ integers() ->
Ts = [{integer,{1,1},I}],
test_string(S, Ts)
end || S <- [[N] || N <- lists:seq($0, $9)] ++ ["2323","000"] ],
+ UnderscoreSamples =
+ [{"123_456", 123456},
+ {"123_456_789", 123456789},
+ {"1_2", 12}],
+ lists:foreach(
+ fun({S, I}) ->
+ test_string(S, [{integer, {1, 1}, I}])
+ end, UnderscoreSamples),
+ UnderscoreErrors =
+ ["123_",
+ "123__",
+ "123_456_",
+ "123__456",
+ "_123",
+ "__123"],
+ lists:foreach(
+ fun(S) ->
+ case erl_scan:string(S) of
+ {ok, [{integer, _, _}], _} ->
+ error({unexpected_integer, S});
+ _ ->
+ ok
+ end
+ end, UnderscoreErrors),
+ test_string("_123", [{var,{1,1},'_123'}]),
+ test_string("123_", [{integer,{1,1},123},{var,{1,4},'_'}]),
ok.
base_integers() ->
@@ -315,13 +341,19 @@ base_integers() ->
{error,{{1,1},erl_scan,{base,1}},{1,2}} =
erl_scan:string("1#000", {1,1}, []),
+ {error,{1,erl_scan,{base,1}},1} = erl_scan:string("1#000"),
+ {error,{{1,1},erl_scan,{base,1000}},{1,6}} =
+ erl_scan:string("1_000#000", {1,1}, []),
+
test_string("12#bc", [{integer,{1,1},11},{atom,{1,5},c}]),
[begin
Str = BS ++ "#" ++ S,
- {error,{1,erl_scan,{illegal,integer}},1} =
- erl_scan:string(Str)
- end || {BS,S} <- [{"3","3"},{"15","f"}, {"12","c"}] ],
+ E = 2 + length(BS),
+ {error,{{1,1},erl_scan,{illegal,integer}},{1,E}} =
+ erl_scan:string(Str, {1,1}, [])
+ end || {BS,S} <- [{"3","3"},{"15","f"},{"12","c"},
+ {"1_5","f"},{"1_2","c"}] ],
{ok,[{integer,1,239},{'@',1}],1} = erl_scan_string("16#ef@"),
{ok,[{integer,{1,1},239},{'@',{1,6}}],{1,7}} =
@@ -329,6 +361,36 @@ base_integers() ->
{ok,[{integer,{1,1},14},{atom,{1,5},g@}],{1,7}} =
erl_scan_string("16#eg@", {1,1}, []),
+ UnderscoreSamples =
+ [{"16#1234_ABCD_EF56", 16#1234abcdef56},
+ {"2#0011_0101_0011", 2#001101010011},
+ {"1_6#123ABC", 16#123abc},
+ {"1_6#123_ABC", 16#123abc},
+ {"16#abcdef", 16#ABCDEF}],
+ lists:foreach(
+ fun({S, I}) ->
+ test_string(S, [{integer, {1, 1}, I}])
+ end, UnderscoreSamples),
+ UnderscoreErrors =
+ ["16_#123ABC",
+ "16#123_",
+ "16#_123",
+ "16#ABC_",
+ "16#_ABC",
+ "2#_0101",
+ "1__6#ABC",
+ "16#AB__CD"],
+ lists:foreach(
+ fun(S) ->
+ case erl_scan:string(S) of
+ {ok, [{integer, _, _}], _} ->
+ error({unexpected_integer, S});
+ _ ->
+ ok
+ end
+ end, UnderscoreErrors),
+ test_string("16#123_", [{integer,{1,1},291},{var,{1,7},'_'}]),
+ test_string("_16#ABC", [{var,{1,1},'_16'},{'#',{1,4}},{var,{1,5},'ABC'}]),
ok.
floats() ->
@@ -344,12 +406,44 @@ floats() ->
erl_scan:string("1.0e400"),
{error,{{1,1},erl_scan,{illegal,float}},{1,8}} =
erl_scan:string("1.0e400", {1,1}, []),
+ {error,{{1,1},erl_scan,{illegal,float}},{1,9}} =
+ erl_scan:string("1.0e4_00", {1,1}, []),
[begin
{error,{1,erl_scan,{illegal,float}},1} = erl_scan:string(S),
{error,{{1,1},erl_scan,{illegal,float}},{1,_}} =
erl_scan:string(S, {1,1}, [])
end || S <- ["1.14Ea"]],
+ UnderscoreSamples =
+ [{"123_456.789", 123456.789},
+ {"123.456_789", 123.456789},
+ {"1.2_345e10", 1.2345e10},
+ {"1.234e1_06", 1.234e106},
+ {"12_34.56_78e1_6", 1234.5678e16},
+ {"12_34.56_78e-1_8", 1234.5678e-18}],
+ lists:foreach(
+ fun({S, I}) ->
+ test_string(S, [{float, {1, 1}, I}])
+ end, UnderscoreSamples),
+ UnderscoreErrors =
+ ["123_.456",
+ "123._456",
+ "123.456_",
+ "123._",
+ "1._23e10",
+ "1.23e_10",
+ "1.23e10_"],
+ lists:foreach(
+ fun(S) ->
+ case erl_scan:string(S) of
+ {ok, [{float, _, _}], _} ->
+ error({unexpected_float, S});
+ _ ->
+ ok
+ end
+ end, UnderscoreErrors),
+ test_string("123._", [{integer,{1,1},123},{'.',{1,4}},{var,{1,5},'_'}]),
+ test_string("1.23_e10", [{float,{1,1},1.23},{var,{1,5},'_e10'}]),
ok.
dots() ->
@@ -1103,6 +1197,11 @@ otp_11807(Config) when is_list(Config) ->
(catch erl_parse:abstract("string", [{encoding,bad}])),
ok.
+otp_16480(Config) when is_list(Config) ->
+ F = fun mod:func/19,
+ F = erl_parse:normalise(erl_parse_abstract(F)),
+ ok.
+
test_string(String, ExpectedWithCol) ->
{ok, ExpectedWithCol, _EndWithCol} = erl_scan_string(String, {1, 1}, []),
Expected = [ begin
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 092cf68bed..73cfada4c0 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -40,16 +40,21 @@
-export([lookup_element_mult/1]).
-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,
- select_bound_chunk/1,
- t_delete_all_objects/1, t_insert_list/1, t_test_ms/1,
+ select_bound_chunk/1, t_delete_all_objects/1, t_test_ms/1,
t_select_delete/1,t_select_replace/1,t_select_replace_next_bug/1,
t_select_pam_stack_overflow_bug/1,
t_ets_dets/1]).
+-export([t_insert_list/1, t_insert_list_bag/1, t_insert_list_duplicate_bag/1,
+ t_insert_list_set/1, t_insert_list_delete_set/1,
+ t_insert_list_parallel/1, t_insert_list_delete_parallel/1,
+ t_insert_list_kill_process/1]).
-export([test_table_size_concurrency/1,test_table_memory_concurrency/1,
- test_delete_table_while_size_snapshot/1, test_delete_table_while_size_snapshot_helper/0]).
+ test_delete_table_while_size_snapshot/1, test_delete_table_while_size_snapshot_helper/1,
+ test_decentralized_counters_setting/1]).
-export([ordered/1, ordered_match/1, interface_equality/1,
- fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1,
+ fixtable_next/1, fixtable_iter_bag/1,
+ fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1,
update_element/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]).
-export([update_counter_with_default/1]).
-export([update_counter_with_default_bad_pos/1]).
@@ -94,6 +99,7 @@
-export([massive_ets_all/1]).
-export([take/1]).
-export([whereis_table/1]).
+-export([ms_excessive_nesting/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% Convenience for manual testing
@@ -130,8 +136,8 @@ all() ->
{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,
+ {group, heavy}, {group, insert_list}, ordered, ordered_match,
+ interface_equality, fixtable_next, fixtable_iter_bag, fixtable_insert,
rename, rename_unnamed, evil_rename, update_element,
update_counter, evil_update_counter,
update_counter_with_default,
@@ -141,7 +147,7 @@ all() ->
match_heavy, {group, fold}, member, t_delete_object,
select_bound_chunk,
t_init_table, t_whitebox, t_delete_all_objects,
- t_insert_list, t_test_ms, t_select_delete, t_select_replace,
+ t_test_ms, t_select_delete, t_select_replace,
t_select_replace_next_bug,
t_select_pam_stack_overflow_bug,
t_ets_dets, memory, t_select_reverse, t_bucket_disappears,
@@ -165,11 +171,13 @@ all() ->
take,
whereis_table,
delete_unfix_race,
- test_throughput_benchmark,
- {group, benchmark},
+ %test_throughput_benchmark,
+ %{group, benchmark},
test_table_size_concurrency,
test_table_memory_concurrency,
- test_delete_table_while_size_snapshot].
+ test_delete_table_while_size_snapshot,
+ test_decentralized_counters_setting,
+ ms_excessive_nesting].
groups() ->
@@ -200,7 +208,12 @@ groups() ->
meta_lookup_named_read, meta_lookup_named_write,
meta_newdel_unnamed, meta_newdel_named]},
{benchmark, [],
- [long_throughput_benchmark]}].
+ [long_throughput_benchmark]},
+ {insert_list, [],
+ [t_insert_list, t_insert_list_set, t_insert_list_bag,
+ t_insert_list_duplicate_bag, t_insert_list_delete_set,
+ t_insert_list_parallel, t_insert_list_delete_parallel,
+ t_insert_list_kill_process]}].
init_per_suite(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -1170,7 +1183,7 @@ t_insert_new(Config) when is_list(Config) ->
L),
verify_etsmem(EtsMem).
-%% Test ets:insert/2 with list of objects.
+%% Test ets:insert/2 with list of objects into duplicate bag table.
t_insert_list(Config) when is_list(Config) ->
EtsMem = etsmem(),
repeat_for_opts(fun t_insert_list_do/1),
@@ -1182,6 +1195,256 @@ t_insert_list_do(Opts) ->
del_one_by_one_dbag_2(T,4000,0),
ets:delete(T).
+% Insert a long list twice in a bag
+t_insert_list_bag(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_bag_do/1,
+ [write_concurrency, compressed]),
+ verify_etsmem(EtsMem).
+
+t_insert_list_bag_do(Opts) ->
+ T = ets:new(t, [bag | Opts]),
+ ListSize = 25000,
+ List = [ {N} || N <- lists:seq(1, ListSize)],
+ ets:insert(T, List),
+ ets:insert(T, List),
+ ListSize = ets:info(T, size),
+
+ %% Insert different sized objects to better test (compressed) object comparison
+ List2 = [begin Bits=(N rem 71), {N div 7, <<N:Bits>>} end || {N} <- List],
+ ets:insert(T, List2),
+ List2Sz = ListSize * 2,
+ List2Sz = ets:info(T, size),
+ ets:delete(T),
+ ok.
+
+% Insert a long list twice in a duplicate_bag
+t_insert_list_duplicate_bag(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ T = ets:new(t, [duplicate_bag]),
+ ListSize = 25000,
+ List = [ {N} || N <- lists:seq(1, ListSize)],
+ ets:insert(T, List),
+ ets:insert(T, List),
+ DoubleListSize = ListSize * 2,
+ DoubleListSize = ets:info(T, size),
+ ets:delete(T),
+ verify_etsmem(EtsMem).
+
+%% Test ets:insert/2 with list of objects into set tables.
+t_insert_list_set(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_set_do/1, [set_types]),
+ verify_etsmem(EtsMem).
+
+t_insert_list_set_do(Opts) ->
+ Nr = 2,
+ t_insert_list_set_do(Opts, fun ets_insert_with_check/2, Nr, 1, Nr+1),
+ t_insert_list_set_do(Opts, fun ets_insert_with_check/2, Nr*2, 2, Nr*2),
+ InsertNewWithCheck =
+ fun(T,E) ->
+ Res = ets:insert_new(T,E),
+ Seq = element(1, lists:nth(1, E)),
+ case Seq rem 2 =:= 0 of
+ true -> Res = false;
+ false -> Res = true
+ end
+ end,
+ t_insert_list_set_do(Opts, InsertNewWithCheck, Nr, 1, Nr),
+ t_insert_list_set_do(Opts, fun ets:insert_new/2, Nr*2, 2, Nr*2),
+ ok.
+
+t_insert_list_set_do(Opts, InsertFun, Nr, Step, ExpectedSize) ->
+ T = ets_new(x,Opts),
+ [InsertFun(T,[{X,X}, {X+1,X}]) || X <- lists:seq(1,Nr,Step)],
+ ExpectedSize = ets:info(T,size),
+ ets:delete(T).
+
+%% Test ets:insert/2 with list of objects into set tables in parallel.
+t_insert_list_parallel(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_parallel_do/1, [[public], set_types]),
+ verify_etsmem(EtsMem).
+
+ets_insert_with_check(Table, ToInsert) ->
+ true = ets:insert(Table, ToInsert),
+ true.
+
+ets_insert_new_with_check(Table, ToInsert) ->
+ ExpectedRes =
+ case put(is_first_insert_for_list, true) of
+ undefined -> true;
+ true -> false
+ end,
+ ExpectedRes = ets:insert_new(Table, ToInsert),
+ ExpectedRes.
+
+t_insert_list_parallel_do(Opts) ->
+ [(fun(I) ->
+ t_insert_list_parallel_do(Opts, I, 2, 100, 500),
+ t_insert_list_parallel_do(Opts, I, 10, 100, 100),
+ t_insert_list_parallel_do(Opts, I, 1000, 100, 10),
+ t_insert_list_parallel_do(Opts, I, 50000, 3, 1)
+ end)(InsertFun) || InsertFun <- [fun ets_insert_with_check/2,
+ fun ets_insert_new_with_check/2]].
+
+t_insert_list_parallel_do(Opts, InsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess) ->
+ T = ets_new(x,Opts),
+ t_insert_list_parallel_do_helper(self(), T, 0, InsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess),
+ receive done -> ok end,
+ ExpectedSize = ListLength * NrOfProcesses,
+ ExpectedSize = length(ets:match_object(T, {'$0', '$1'})),
+ ExpectedSize = ets:info(T, size),
+ ets:delete(T),
+ ok.
+
+t_insert_list_delete_parallel(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_delete_parallel_do/1, [[public], set_types]),
+ verify_etsmem(EtsMem).
+
+t_insert_list_delete_parallel_do(Opts) ->
+ [(fun(I) ->
+ t_insert_list_delete_parallel_do(Opts, I, 30, 32, 1000000),
+ t_insert_list_delete_parallel_do(Opts, I, 300, 8, 1000000),
+ t_insert_list_delete_parallel_do(Opts, I, 3000, 4, 1000000),
+ t_insert_list_delete_parallel_do(Opts, I, 9000, 4, 1000000)
+ end)(InsertFun) || InsertFun <- [fun ets_insert_with_check/2,
+ fun ets_insert_new_with_check/2]],
+ ok.
+
+t_insert_list_delete_parallel_do(Opts, InsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess) ->
+ T = ets_new(x,Opts),
+ CompletedInsertsCtr = counters:new(1,[]),
+ NewInsertFun =
+ fun(Table, ToInsert) ->
+ try
+ InsertFun(Table, ToInsert),
+ counters:add(CompletedInsertsCtr, 1, 1)
+ catch
+ error:badarg -> put(stop,yes)
+ end
+ end,
+ Self = self(),
+ spawn(fun()->
+ t_insert_list_parallel_do_helper(self(), T, 0, NewInsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess),
+ receive done -> Self ! done_parallel_insert end
+ end),
+ receive after 3 -> ok end,
+ spawn(fun()->
+ spawn(fun()->
+ receive after 7 -> ok end,
+ ets:delete(T),
+ Self ! done_delete
+ end)
+ end),
+ receive done_delete -> ok end,
+ receive done_parallel_insert -> ok end,
+ io:format("~p/~p completed",
+ [counters:get(CompletedInsertsCtr, 1),
+ NrOfProcesses * NrOfInsertsPerProcess]).
+
+
+t_insert_list_parallel_do_helper(Parent, T, StartKey, InsertFun, ListLength, 1, NrOfInsertsPerProcess) ->
+ try
+ repeat(fun()->
+ case get(stop) of
+ yes -> throw(end_repeat);
+ _ -> ok
+ end,
+ InsertFun(T,[{X,X} || X <- lists:seq(StartKey,StartKey+ListLength-1,1)])
+ end, NrOfInsertsPerProcess)
+ catch
+ throw:end_repeat -> ok
+ end,
+ Parent ! done;
+t_insert_list_parallel_do_helper(Parent, T, StartKey, InsertFun, ListLength, NrOfProcesses, NrOfInsertsPerProcess) ->
+ Self = self(),
+ spawn(fun() ->
+ t_insert_list_parallel_do_helper(Self,
+ T,
+ StartKey,
+ InsertFun,
+ ListLength,
+ NrOfProcesses div 2,
+ NrOfInsertsPerProcess) end),
+ spawn(fun() ->
+ t_insert_list_parallel_do_helper(Self,
+ T,
+ StartKey + ListLength*(NrOfProcesses div 2),
+ InsertFun,
+ ListLength,
+ (NrOfProcesses div 2) + (NrOfProcesses rem 2),
+ NrOfInsertsPerProcess)
+ end),
+ receive done -> ok end,
+ receive done -> ok end,
+ Parent ! done.
+
+t_insert_list_delete_set(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_delete_set_do/1, [[public],set_types]),
+ verify_etsmem(EtsMem).
+
+t_insert_list_delete_set_do(Opts) ->
+ [(fun(I) ->
+ t_insert_list_delete_set_do(Opts, I, 1000000, 1, 1),
+ t_insert_list_delete_set_do(Opts, I, 100000, 10, 5),
+ t_insert_list_delete_set_do(Opts, I, 10000, 100, 50),
+ t_insert_list_delete_set_do(Opts, I, 1000, 1000, 500)
+ end)(InsertFun) || InsertFun <- [fun ets_insert_with_check/2,
+ fun ets_insert_new_with_check/2]],
+ ok.
+
+
+t_insert_list_delete_set_do(Opts, InsertFun, ListLength, NrOfTables, NrOfInserts) ->
+ CompletedInsertsCtr = counters:new(1,[]),
+ Parent = self(),
+ [(fun() ->
+ T = ets_new(x,Opts),
+ spawn(
+ fun() ->
+ try
+ repeat(
+ fun() ->
+ InsertFun(T,[{Z,Z} ||
+ Z <- lists:seq(1,ListLength)]),
+ counters:add(CompletedInsertsCtr, 1, 1)%,
+ end, NrOfInserts)
+ catch
+ error:badarg -> ok
+ end,
+ Parent ! done
+ end),
+ receive after 1 -> ok end,
+ ets:delete(T)
+ end)() || _ <- lists:seq(1,NrOfTables)],
+ [receive done -> ok end || _ <- lists:seq(1,NrOfTables)],
+ io:format("~p/~p completed",
+ [counters:get(CompletedInsertsCtr, 1),
+ NrOfTables * NrOfInserts]).
+
+
+t_insert_list_kill_process(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ repeat_for_opts(fun t_insert_list_kill_process_do/1, [[public], set_types]),
+ verify_etsmem(EtsMem).
+
+
+t_insert_list_kill_process_do(Opts) ->
+ [(fun(I) ->
+ [(fun(Time) ->
+ T = ets_new(x,Opts),
+ List = lists:seq(1,600000),
+ TupleList = [{E,E} || E <- List],
+ Pid = spawn(fun() -> I(T, TupleList) end),
+ receive after Time -> ok end,
+ exit(Pid, kill),
+ ets:delete(T)
+ end)(TheTime) || TheTime <- [1,3,5] ++ lists:seq(7,29,7)]
+ end)(InsertFun) || InsertFun <- [fun ets:insert/2,
+ fun ets:insert_new/2]],
+ ok.
%% Test interface of ets:test_ms/2.
t_test_ms(Config) when is_list(Config) ->
@@ -1393,7 +1656,11 @@ t_select_delete(Config) when is_list(Config) ->
%% Tests the ets:select_replace/2 BIF
t_select_replace(Config) when is_list(Config) ->
EtsMem = etsmem(),
- Tables = fill_sets_int(10000) ++ fill_sets_int(10000, [{write_concurrency,true}]),
+ repeat_for_opts(fun do_select_replace/1),
+ verify_etsmem(EtsMem).
+
+do_select_replace(Opts) ->
+ Tables = fill_sets_intup(10000, Opts),
TestFun = fun (Table, TableType) when TableType =:= bag ->
% Operation not supported; bag implementation
@@ -1402,80 +1669,80 @@ t_select_replace(Config) when is_list(Config) ->
(Table, TableType) ->
% Invalid replacement doesn't keep the key
- MatchSpec1 = [{{'$1', '$2'},
+ MatchSpec1 = [{{{'$1','$3'}, '$2'},
[{'=:=', {'band', '$1', 2#11}, 2#11},
{'=/=', {'hd', '$2'}, $x}],
- [{{'$2', '$1'}}]}],
+ [{{{{'$2','$3'}}, '$1'}}]}],
{'EXIT',{badarg,_}} = (catch ets:select_replace(Table, MatchSpec1)),
% Invalid replacement doesn't keep the key (even though it would be the same value)
- MatchSpec2 = [{{'$1', '$2'},
+ MatchSpec2 = [{{{'$1','$3'}, '$2'},
[{'=:=', {'band', '$1', 2#11}, 2#11}],
- [{{{'+', '$1', 0}, '$2'}}]},
- {{'$1', '$2'},
+ [{{{{{'+', '$1', 0},'$3'}}, '$2'}}]},
+ {{{'$1','$3'}, '$2'},
[{'=/=', {'band', '$1', 2#11}, 2#11}],
- [{{{'-', '$1', 0}, '$2'}}]}],
+ [{{{{{'-', '$1', 0},'$3'}}, '$2'}}]}],
{'EXIT',{badarg,_}} = (catch ets:select_replace(Table, MatchSpec2)),
% Invalid replacement changes key to float equivalent
- MatchSpec3 = [{{'$1', '$2'},
+ MatchSpec3 = [{{{'$1','$3'}, '$2'},
[{'=:=', {'band', '$1', 2#11}, 2#11},
{'=/=', {'hd', '$2'}, $x}],
- [{{{'*', '$1', 1.0}, '$2'}}]}],
+ [{{{{{'*', '$1', 1.0},'$3'}}, '$2'}}]}],
{'EXIT',{badarg,_}} = (catch ets:select_replace(Table, MatchSpec3)),
% Replacements are differently-sized tuples
- MatchSpec4_A = [{{'$1','$2'},
+ MatchSpec4_A = [{{{'$1','$3'},'$2'},
[{'<', {'rem', '$1', 5}, 2}],
- [{{'$1', [$x | '$2'], stuff}}]}],
- MatchSpec4_B = [{{'$1','$2','_'},
+ [{{{{'$1','$3'}}, [$x | '$2'], stuff}}]}],
+ MatchSpec4_B = [{{{'$1','$3'},'$2','_'},
[],
- [{{'$1','$2'}}]}],
+ [{{{{'$1','$3'}},'$2'}}]}],
4000 = ets:select_replace(Table, MatchSpec4_A),
4000 = ets:select_replace(Table, MatchSpec4_B),
% Replacement is the same tuple
- MatchSpec5 = [{{'$1', '$2'},
+ MatchSpec5 = [{{{'$1','$3'}, '$2'},
[{'>', {'rem', '$1', 5}, 3}],
['$_']}],
2000 = ets:select_replace(Table, MatchSpec5),
% Replacement reconstructs an equal tuple
- MatchSpec6 = [{{'$1', '$2'},
+ MatchSpec6 = [{{{'$1','$3'}, '$2'},
[{'>', {'rem', '$1', 5}, 3}],
- [{{'$1', '$2'}}]}],
+ [{{{{'$1','$3'}}, '$2'}}]}],
2000 = ets:select_replace(Table, MatchSpec6),
% Replacement uses {element,KeyPos,T} for key
2000 = ets:select_replace(Table,
- [{{'$1', '$2'},
+ [{{{'$1','$3'}, '$2'},
[{'>', {'rem', '$1', 5}, 3}],
[{{{element, 1, '$_'}, '$2'}}]}]),
% Replacement uses wrong {element,KeyPos,T} for key
{'EXIT',{badarg,_}} = (catch ets:select_replace(Table,
- [{{'$1', '$2'},
+ [{{{'$1','$3'}, '$2'},
[],
[{{{element, 2, '$_'}, '$2'}}]}])),
check(Table,
- fun ({N, [$x, C | _]}) when ((N rem 5) < 2) -> (C >= $0) andalso (C =< $9);
- ({N, [C | _]}) when is_float(N) -> (C >= $0) andalso (C =< $9);
- ({N, [C | _]}) when ((N rem 5) > 3) -> (C >= $0) andalso (C =< $9);
+ fun ({{N,_}, [$x, C | _]}) when ((N rem 5) < 2) -> (C >= $0) andalso (C =< $9);
+ ({{N,_}, [C | _]}) when is_float(N) -> (C >= $0) andalso (C =< $9);
+ ({{N,_}, [C | _]}) when ((N rem 5) > 3) -> (C >= $0) andalso (C =< $9);
({_, [C | _]}) -> (C >= $0) andalso (C =< $9)
end,
10000),
% Replace unbound range (>)
- MatchSpec7 = [{{'$1', '$2'},
+ MatchSpec7 = [{{{'$1','$3'}, '$2'},
[{'>', '$1', 7000}],
- [{{'$1', {{gt_range, '$2'}}}}]}],
+ [{{{{'$1','$3'}}, {{gt_range, '$2'}}}}]}],
3000 = ets:select_replace(Table, MatchSpec7),
% Replace unbound range (<)
- MatchSpec8 = [{{'$1', '$2'},
+ MatchSpec8 = [{{{'$1','$3'}, '$2'},
[{'<', '$1', 3000}],
- [{{'$1', {{le_range, '$2'}}}}]}],
+ [{{{{'$1','$3'}}, {{le_range, '$2'}}}}]}],
case TableType of
ordered_set -> 2999 = ets:select_replace(Table, MatchSpec8);
set -> 2999 = ets:select_replace(Table, MatchSpec8);
@@ -1483,10 +1750,10 @@ t_select_replace(Config) when is_list(Config) ->
end,
% Replace bound range
- MatchSpec9 = [{{'$1', '$2'},
+ MatchSpec9 = [{{{'$1','$3'}, '$2'},
[{'>=', '$1', 3001},
{'<', '$1', 7000}],
- [{{'$1', {{range, '$2'}}}}]}],
+ [{{{{'$1','$3'}}, {{range, '$2'}}}}]}],
case TableType of
ordered_set -> 3999 = ets:select_replace(Table, MatchSpec9);
set -> 3999 = ets:select_replace(Table, MatchSpec9);
@@ -1494,12 +1761,12 @@ t_select_replace(Config) when is_list(Config) ->
end,
% Replace particular keys
- MatchSpec10 = [{{'$1', '$2'},
+ MatchSpec10 = [{{{'$1','$3'}, '$2'},
[{'==', '$1', 3000}],
- [{{'$1', {{specific1, '$2'}}}}]},
- {{'$1', '$2'},
+ [{{{{'$1','$3'}}, {{specific1, '$2'}}}}]},
+ {{{'$1','$3'}, '$2'},
[{'==', '$1', 7000}],
- [{{'$1', {{specific2, '$2'}}}}]}],
+ [{{{{'$1','$3'}}, {{specific2, '$2'}}}}]}],
case TableType of
ordered_set -> 2 = ets:select_replace(Table, MatchSpec10);
set -> 2 = ets:select_replace(Table, MatchSpec10);
@@ -1507,11 +1774,11 @@ t_select_replace(Config) when is_list(Config) ->
end,
check(Table,
- fun ({N, {gt_range, _}}) -> N > 7000;
- ({N, {le_range, _}}) -> N < 3000;
- ({N, {range, _}}) -> (N >= 3001) andalso (N < 7000);
- ({N, {specific1, _}}) -> N == 3000;
- ({N, {specific2, _}}) -> N == 7000
+ fun ({{N,_}, {gt_range, _}}) -> N > 7000;
+ ({{N,_}, {le_range, _}}) -> N < 3000;
+ ({{N,_}, {range, _}}) -> (N >= 3001) andalso (N < 7000);
+ ({{N,_}, {specific1, _}}) -> N == 3000;
+ ({{N,_}, {specific2, _}}) -> N == 7000
end,
10000),
@@ -1551,7 +1818,7 @@ t_select_replace(Config) when is_list(Config) ->
]
end,
- T2 = ets:new(x, []),
+ T2 = ets:new(x, Opts),
[lists:foreach(fun({A, B}) ->
%% just check that matchspec is accepted
0 = ets:select_replace(T2, [{{A, '$2', '$3'}, [], [{{B, '$3', '$2'}}]}])
@@ -1612,8 +1879,7 @@ t_select_replace(Config) when is_list(Config) ->
ets:delete(T2),
-
- verify_etsmem(EtsMem).
+ ok.
%% OTP-15346: Bug caused select_replace of bound key to corrupt static stack
%% used by ets:next and ets:prev.
@@ -2506,6 +2772,135 @@ do_fixtable_next(Tab) ->
false = ets:info(Tab, fixed),
ets:delete(Tab).
+%% Check that iteration of bags find all live objects and nothing else.
+fixtable_iter_bag(Config) when is_list(Config) ->
+ repeat_for_opts(fun fixtable_iter_do/1,
+ [write_concurrency,[bag,duplicate_bag]]).
+
+fixtable_iter_do(Opts) ->
+ EtsMem = etsmem(),
+ do_fixtable_iter_bag(ets_new(fixtable_iter_bag,Opts)),
+ verify_etsmem(EtsMem).
+
+do_fixtable_iter_bag(T) ->
+ MaxValues = 4,
+ %% Create 1 to MaxValues objects for each key
+ %% and then delete every possible combination of those objects
+ %% in every possible order.
+ %% Then test iteration returns all live objects and nothing else.
+
+ CrDelOps = [begin
+ Values = lists:seq(1,N),
+ %% All ways of deleting any number of the Values in any order
+ Combos = combs(Values),
+ DeleteOps = concat_lists([perms(C) || C <- Combos]),
+ {N, DeleteOps}
+ end
+ || N <- lists:seq(1,MaxValues)],
+
+ %%io:format("~p\n", [CrDelOps]),
+
+ NKeys = lists:foldl(fun({_, DeleteOps}, Cnt) ->
+ Cnt + length(DeleteOps)
+ end,
+ 0,
+ CrDelOps),
+
+ io:format("Create ~p keys\n", [NKeys]),
+
+ %% Fixate even before inserts just to maintain small table size
+ %% and increase likelyhood of different keys in same bucket.
+ ets:safe_fixtable(T,true),
+ InsRes = [begin
+ [begin
+ Key = {NValues,ValueList},
+ [begin
+ Tpl = {Key, V},
+ %%io:format("Insert object ~p", [Tpl]),
+ ets:insert(T, Tpl),
+ Tpl
+ end
+ || V <- lists:seq(1,NValues)]
+ end
+ || ValueList <- DeleteOps]
+ end
+ || {NValues, DeleteOps} <- CrDelOps],
+
+ Inserted = lists:flatten(InsRes),
+ InSorted = lists:sort(Inserted),
+ InSorted = lists:usort(Inserted), %% No duplicates
+ NObjs = length(Inserted),
+
+ DelRes = [begin
+ [begin
+ Key = {NValues,ValueList},
+ [begin
+ Tpl = {Key, V},
+ %%io:format("Delete object ~p", [Tpl]),
+ ets:delete_object(T, Tpl),
+ Tpl
+ end
+ || V <- ValueList]
+ end
+ || ValueList <- DeleteOps]
+ end
+ || {NValues, DeleteOps} <- CrDelOps],
+
+ Deleted = lists:flatten(DelRes),
+ DelSorted = lists:sort(Deleted),
+ DelSorted = lists:usort(Deleted), %% No duplicates
+ NDels = length(Deleted),
+
+ %% Nr of keys where all values were deleted.
+ NDeletedKeys = lists:sum([factorial(N) || N <- lists:seq(1,MaxValues)]),
+
+ CountKeysFun = fun Me(K1, Cnt) ->
+ case ets:next(T, K1) of
+ '$end_of_table' ->
+ Cnt;
+ K2 ->
+ Objs = ets:lookup(T, K2),
+ [{{NValues, ValueList}, _V} | _] = Objs,
+ ExpectedLive = NValues - length(ValueList),
+ ExpectedLive = length(Objs),
+ Me(K2, Cnt+1)
+ end
+ end,
+
+ ExpectedKeys = NKeys - NDeletedKeys,
+ io:format("Expected keys: ~p\n", [ExpectedKeys]),
+ FoundKeys = CountKeysFun(ets:first(T), 1),
+ io:format("Found keys: ~p\n", [FoundKeys]),
+ ExpectedKeys = FoundKeys,
+
+ ExpectedObjs = NObjs - NDels,
+ io:format("Expected objects: ~p\n", [ExpectedObjs]),
+ FoundObjs = ets:select_count(T, [{{'_','_'}, [], [true]}]),
+ io:format("Found objects: ~p\n", [FoundObjs]),
+ ExpectedObjs = FoundObjs,
+
+ ets:delete(T).
+
+%% All permutations of list
+perms([]) -> [[]];
+perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].
+
+%% All combinations of picking the element (or not) from list
+combs([]) -> [[]];
+combs([H|T]) ->
+ Tcombs = combs(T),
+ Tcombs ++ [[H | C] || C <- Tcombs].
+
+factorial(0) -> 1;
+factorial(N) when N > 0 ->
+ N * factorial(N - 1).
+
+concat_lists([]) ->
+ [];
+concat_lists([H|T]) ->
+ H ++ concat_lists(T).
+
+
%% Check inserts of deleted keys in fixed bags.
fixtable_insert(Config) when is_list(Config) ->
Combos = [[Type,{write_concurrency,WC}] || Type<- [bag,duplicate_bag],
@@ -2570,15 +2965,17 @@ write_concurrency(Config) when is_list(Config) ->
Yes6 = ets_new(foo,[duplicate_bag,protected,{write_concurrency,true}]),
No3 = ets_new(foo,[duplicate_bag,private,{write_concurrency,true}]),
- Yes7 = ets_new(foo,[ordered_set,public,{write_concurrency,true}]),
- Yes8 = ets_new(foo,[ordered_set,protected,{write_concurrency,true}]),
- Yes9 = ets_new(foo,[ordered_set,{write_concurrency,true}]),
- Yes10 = ets_new(foo,[{write_concurrency,true},ordered_set,public]),
- Yes11 = ets_new(foo,[{write_concurrency,true},ordered_set,protected]),
+ NoCentCtrs = {decentralized_counters,false},
+ Yes7 = ets_new(foo,[ordered_set,public,{write_concurrency,true},NoCentCtrs]),
+ Yes8 = ets_new(foo,[ordered_set,protected,{write_concurrency,true},NoCentCtrs]),
+ Yes9 = ets_new(foo,[ordered_set,{write_concurrency,true},NoCentCtrs]),
+ Yes10 = ets_new(foo,[{write_concurrency,true},ordered_set,public,NoCentCtrs]),
+ Yes11 = ets_new(foo,[{write_concurrency,true},ordered_set,protected,NoCentCtrs]),
Yes12 = ets_new(foo,[set,{write_concurrency,false},
- {write_concurrency,true},ordered_set,public]),
+ {write_concurrency,true},ordered_set,public,NoCentCtrs]),
Yes13 = ets_new(foo,[private,public,set,{write_concurrency,false},
- {write_concurrency,true},ordered_set]),
+ {write_concurrency,true},ordered_set,NoCentCtrs]),
+ Yes14 = ets_new(foo,[ordered_set,public,{write_concurrency,true}]),
No4 = ets_new(foo,[ordered_set,private,{write_concurrency,true}]),
No5 = ets_new(foo,[ordered_set,public,{write_concurrency,false}]),
No6 = ets_new(foo,[ordered_set,protected,{write_concurrency,false}]),
@@ -2590,6 +2987,7 @@ write_concurrency(Config) when is_list(Config) ->
YesMem = ets:info(Yes1,memory),
NoHashMem = ets:info(No1,memory),
YesTreeMem = ets:info(Yes7,memory),
+ YesYesTreeMem = ets:info(Yes14,memory),
NoTreeMem = ets:info(No4,memory),
io:format("YesMem=~p NoHashMem=~p NoTreeMem=~p YesTreeMem=~p\n",[YesMem,NoHashMem,
NoTreeMem,YesTreeMem]),
@@ -2615,10 +3013,17 @@ write_concurrency(Config) when is_list(Config) ->
NoHashMem = ets:info(No8,memory),
NoHashMem = ets:info(No9,memory),
- true = YesMem > NoHashMem orelse erlang:system_info(schedulers) == 1,
- true = YesMem > NoTreeMem orelse erlang:system_info(schedulers) == 1,
true = YesMem > YesTreeMem,
- true = YesTreeMem < NoTreeMem orelse erlang:system_info(schedulers) == 1,
+
+ case erlang:system_info(schedulers) > 1 of
+ true ->
+ true = YesMem > NoHashMem,
+ true = YesMem > NoTreeMem,
+ true = YesTreeMem < NoTreeMem,
+ true = YesYesTreeMem > YesTreeMem;
+ _ ->
+ one_scheduler_only
+ end,
{'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,foo}])),
{'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency}])),
@@ -2626,7 +3031,7 @@ write_concurrency(Config) when is_list(Config) ->
{'EXIT',{badarg,_}} = (catch ets_new(foo,[public,write_concurrency])),
lists:foreach(fun(T) -> ets:delete(T) end,
- [Yes1,Yes2,Yes3,Yes4,Yes5,Yes6,Yes7,Yes8,Yes9,Yes10,Yes11,Yes12,Yes13,
+ [Yes1,Yes2,Yes3,Yes4,Yes5,Yes6,Yes7,Yes8,Yes9,Yes10,Yes11,Yes12,Yes13,Yes14,
No1,No2,No3,No4,No5,No6,No7,No8,No9]),
verify_etsmem(EtsMem),
ok.
@@ -4509,6 +4914,8 @@ info_do(Opts) ->
{value, {protection, Protection}} =
lists:keysearch(protection, 1, Res),
{value, {id, Tab}} = lists:keysearch(id, 1, Res),
+ {value, {decentralized_counters, _DecentralizedCtrs}} =
+ lists:keysearch(decentralized_counters, 1, Res),
%% Test 'binary'
[] = ?ets_info(Tab, binary, SlavePid),
@@ -4622,7 +5029,10 @@ size_loop(_T, 0, _, _) ->
size_loop(T, I, PrevSize, WhatToTest) ->
Size = ets:info(T, WhatToTest),
case Size < PrevSize of
- true -> ct:fail("Bad ets:info/2");
+ true ->
+ io:format("Bad ets:info/2 (got ~p expected >=~p)",
+ [Size, PrevSize]),
+ ct:fail("Bad ets:info/2)");
_ -> ok
end,
size_loop(T, I -1, Size, WhatToTest).
@@ -4634,13 +5044,17 @@ add_loop(T, I) ->
add_loop(T, I -1).
-test_table_counter_concurrency(WhatToTest) ->
+test_table_counter_concurrency(WhatToTest, TableOptions) ->
IntStatePrevOn =
erts_debug:set_internal_state(available_internal_state, true),
ItemsToAdd = 1000000,
SizeLoopSize = 1000,
- T = ets:new(k, [public, ordered_set, {write_concurrency, true}]),
- erts_debug:set_internal_state(ets_debug_random_split_join, {T, false}),
+ T = ets:new(k, TableOptions),
+ case lists:member(ordered_set, TableOptions) of
+ true ->
+ erts_debug:set_internal_state(ets_debug_random_split_join, {T, false});
+ false -> ok
+ end,
0 = ets:info(T, size),
P = self(),
SpawnedSizeProcs =
@@ -4671,14 +5085,18 @@ test_table_size_concurrency(Config) when is_list(Config) ->
case erlang:system_info(schedulers) of
1 -> {skip,"Only valid on smp > 1 systems"};
_ ->
- test_table_counter_concurrency(size)
+ BaseOptions = [public, {write_concurrency, true}],
+ test_table_counter_concurrency(size, [set | BaseOptions]),
+ test_table_counter_concurrency(size, [ordered_set | BaseOptions])
end.
test_table_memory_concurrency(Config) when is_list(Config) ->
case erlang:system_info(schedulers) of
1 -> {skip,"Only valid on smp > 1 systems"};
_ ->
- test_table_counter_concurrency(memory)
+ BaseOptions = [public, {write_concurrency, true}],
+ test_table_counter_concurrency(memory, [set | BaseOptions]),
+ test_table_counter_concurrency(memory, [ordered_set | BaseOptions])
end.
%% Tests that calling the ets:delete operation on a table T with
@@ -4689,15 +5107,20 @@ test_delete_table_while_size_snapshot(Config) when is_list(Config) ->
%% depend on that pids are ordered in creation order which is no
%% longer the case when many processes have been started before
Node = start_slave(),
- ok = rpc:call(Node, ?MODULE, test_delete_table_while_size_snapshot_helper, []),
+ [ok = rpc:call(Node,
+ ?MODULE,
+ test_delete_table_while_size_snapshot_helper,
+ [TableType])
+ || TableType <- [set, ordered_set]],
test_server:stop_node(Node),
ok.
-test_delete_table_while_size_snapshot_helper()->
+test_delete_table_while_size_snapshot_helper(TableType) ->
TopParent = self(),
repeat_par(
fun() ->
- Table = ets:new(t, [public, ordered_set,
+ Table = ets:new(t, [public, TableType,
+ {decentralized_counters, true},
{write_concurrency, true}]),
Parent = self(),
NrOfSizeProcs = 100,
@@ -4705,12 +5128,12 @@ test_delete_table_while_size_snapshot_helper()->
|| _ <- lists:seq(1, NrOfSizeProcs)],
timer:sleep(1),
ets:delete(Table),
- [receive
+ [receive
table_gone -> ok;
Problem -> TopParent ! Problem
end || _ <- Pids]
end,
- 15000),
+ 100*erlang:system_info(schedulers_online)),
receive
Problem -> throw(Problem)
after 0 -> ok
@@ -4750,6 +5173,64 @@ repeat_par_help(FunToRepeat, NrOfTimes, OrgNrOfTimes) ->
end),
repeat_par_help(FunToRepeat, NrOfTimes-1, OrgNrOfTimes).
+test_decentralized_counters_setting(Config) when is_list(Config) ->
+ case erlang:system_info(schedulers) of
+ 1 -> {skip,"Only relevant when the number of shedulers > 1"};
+ _ -> EtsMem = etsmem(),
+ do_test_decentralized_counters_setting(set),
+ do_test_decentralized_counters_setting(ordered_set),
+ do_test_decentralized_counters_default_setting(),
+ verify_etsmem(EtsMem)
+ end.
+
+do_test_decentralized_counters_setting(TableType) ->
+ wait_for_memory_deallocations(),
+ FlxCtrMemUsage = erts_debug:get_internal_state(flxctr_memory_usage),
+ lists:foreach(
+ fun(OptList) ->
+ T1 = ets:new(t1, [public, TableType] ++ OptList ++ [TableType]),
+ check_decentralized_counters(T1, false, FlxCtrMemUsage),
+ ets:delete(T1)
+ end,
+ [[{write_concurrency, false}],
+ [{write_concurrency, true}, {decentralized_counters, false}]]),
+ lists:foreach(
+ fun(OptList) ->
+ T1 = ets:new(t1, [public,
+ TableType,
+ {write_concurrency, true}] ++ OptList ++ [TableType]),
+ check_decentralized_counters(T1, true, FlxCtrMemUsage),
+ ets:delete(T1),
+ wait_for_memory_deallocations(),
+ FlxCtrMemUsage = erts_debug:get_internal_state(flxctr_memory_usage)
+ end,
+ [[{decentralized_counters, true}]]),
+ ok.
+
+do_test_decentralized_counters_default_setting() ->
+ wait_for_memory_deallocations(),
+ FlxCtrMemUsage = erts_debug:get_internal_state(flxctr_memory_usage),
+ Set = ets:new(t1, [public, {write_concurrency, true}]),
+ check_decentralized_counters(Set, false, FlxCtrMemUsage),
+ ets:delete(Set),
+ Set2 = ets:new(t1, [public, set, {write_concurrency, true}]),
+ check_decentralized_counters(Set2, false, FlxCtrMemUsage),
+ ets:delete(Set2),
+ OrdSet = ets:new(t1, [public, ordered_set, {write_concurrency, true}]),
+ check_decentralized_counters(OrdSet, true, FlxCtrMemUsage),
+ ets:delete(OrdSet),
+ ok.
+
+check_decentralized_counters(T, ExpectedState, InitMemUsage) ->
+ case {ExpectedState, erts_debug:get_internal_state(flxctr_memory_usage)} of
+ {false, notsup} -> ok;
+ {false, X} -> InitMemUsage = X;
+ {true, notsup} -> ok;
+ {true, X} when X > InitMemUsage -> ok;
+ {true, _} -> ct:fail("Decentralized counter not used.")
+ end,
+ ExpectedState = ets:info(T, decentralized_counters).
+
%% Test various duplicate_bags stuff.
dups(Config) when is_list(Config) ->
repeat_for_opts(fun dups_do/1).
@@ -5035,7 +5516,7 @@ tabfile_ext4(Config) when is_list(Config) ->
{error,Y} = ets:file2tab(FName,[{verify,true}]),
ets:tab2file(TL,FName,[{extended_info,[md5sum]}]),
{X,Y}
- end || N <- lists:seq(500,600)],
+ end || N <- lists:seq(700,800)],
io:format("~p~n",[Res]),
file:delete(FName)
end),
@@ -5436,6 +5917,7 @@ make_table(Name, Options, Elements) ->
T = ets_new(Name, Options),
lists:foreach(fun(E) -> ets:insert(T, E) end, Elements),
T.
+
filltabint(Tab,0) ->
Tab;
filltabint(Tab,N) ->
@@ -5463,6 +5945,22 @@ xfilltabint(Tab,N) ->
filltabint(Tab,N)
end.
+filltabintup(Tab,0) ->
+ Tab;
+filltabintup(Tab,N) ->
+ ets:insert(Tab,{{N,integer_to_list(N)},integer_to_list(N)}),
+ filltabintup(Tab,N-1).
+
+filltabintup2(Tab,0) ->
+ Tab;
+filltabintup2(Tab,N) ->
+ ets:insert(Tab,{{N + N rem 2,integer_to_list(N)},integer_to_list(N)}),
+ filltabintup2(Tab,N-1).
+filltabintup3(Tab,0) ->
+ Tab;
+filltabintup3(Tab,N) ->
+ ets:insert(Tab,{{N + N rem 2,integer_to_list(N + N rem 2)},integer_to_list(N + N rem 2)}),
+ filltabintup3(Tab,N-1).
filltabstr(Tab,N) ->
filltabstr(Tab,0,N).
@@ -5508,6 +6006,19 @@ fill_sets_int(N,Opts) ->
filltabint3(Tab4,N),
[Tab1,Tab2,Tab3,Tab4].
+fill_sets_intup(N) ->
+ fill_sets_int(N,[]).
+fill_sets_intup(N,Opts) ->
+ Tab1 = ets_new(xxx, [ordered_set|Opts]),
+ filltabintup(Tab1,N),
+ Tab2 = ets_new(xxx, [set|Opts]),
+ filltabintup(Tab2,N),
+ Tab3 = ets_new(xxx, [bag|Opts]),
+ filltabintup2(Tab3,N),
+ Tab4 = ets_new(xxx, [duplicate_bag|Opts]),
+ filltabintup3(Tab4,N),
+ [Tab1,Tab2,Tab3,Tab4].
+
check_fun(_Tab,_Fun,'$end_of_table') ->
ok;
check_fun(Tab,Fun,Item) ->
@@ -5816,13 +6327,13 @@ otp_7665_act(Tab,Min,Max,DelNr) ->
true = ets:insert(Tab, List1),
true = ets:safe_fixtable(Tab, true),
true = ets:delete_object(Tab, {key,DelNr}),
- List2 = lists:delete({key,DelNr}, List1),
+ List2 = lists:sort(lists:delete({key,DelNr}, List1)),
%% Now verify that we find all remaining objects
- List2 = ets:lookup(Tab,key),
- EList2 = lists:map(fun({key,N})-> N end,
- List2),
- EList2 = ets:lookup_element(Tab,key,2),
+ List2 = lists:sort(ets:lookup(Tab,key)),
+ EList2 = lists:sort(lists:map(fun({key,N})-> N end,
+ List2)),
+ EList2 = lists:sort(ets:lookup_element(Tab,key,2)),
true = ets:delete(Tab, key),
[] = ets:lookup(Tab, key),
true = ets:safe_fixtable(Tab, false),
@@ -6387,7 +6898,7 @@ verify_table_load(T) ->
false;
true ->
- io:format("Stats = ~p\n",[Stats]),
+ io:format("Stats = ~p\n~p\n",[Stats, ets:info(T)]),
ok
end
end.
@@ -6475,7 +6986,8 @@ smp_select_delete_do(Opts) ->
smp_select_replace(Config) when is_list(Config) ->
repeat_for_opts(fun smp_select_replace_do/1,
- [[set,ordered_set,stim_cat_ord_set,duplicate_bag]]).
+ [[set,ordered_set,stim_cat_ord_set,duplicate_bag],
+ compressed]).
smp_select_replace_do(Opts) ->
KeyRange = 20,
@@ -6847,7 +7359,8 @@ take(Config) when is_list(Config) ->
%% Same with bag.
T3 = ets_new(c, [bag]),
ets:insert(T3, [{1,1},{1,2},{3,3}]),
- [{1,1},{1,2}] = ets:take(T3, 1),
+ R = lists:sort([{1,1},{1,2}]),
+ R = lists:sort(ets:take(T3, 1)),
[{3,3}] = ets:take(T3, 3),
[] = ets:tab2list(T3),
ets:delete(T1),
@@ -6885,6 +7398,50 @@ whereis_table(Config) when is_list(Config) ->
ok.
+ms_excessive_nesting(Config) when is_list(Config) ->
+ MkMSCond = fun (_Fun, N) when N < 0 -> true;
+ (Fun, N) -> {'orelse', {'==', N, '$1'}, Fun(Fun, N-1)}
+ end,
+ %% Ensure it compiles with substantial but reasonable
+ %% (hmm...) nesting
+ MS = [{{'$1', '$2'}, [MkMSCond(MkMSCond, 100)], [{{'$1', blipp}}]}],
+ io:format("~p~n", [erlang:match_spec_test({1, blupp}, MS, table)]),
+ _ = ets:match_spec_compile(MS),
+ %% Now test match_spec_compile() and select_replace()
+ %% with tree and hash using excessive nesting. These
+ %% used to seg-fault the emulator due to recursion
+ %% beyond the end of the C-stack.
+ %%
+ %% We expect to get a system_limit error, but don't
+ %% fail if it compiles (someone must have rewritten
+ %% compilation of match specs to use an explicit
+ %% stack instead of using recursion).
+ ENMS = [{{'$1', '$2'}, [MkMSCond(MkMSCond, 1000000)], [{{'$1', blipp}}]}],
+ io:format("~p~n", [erlang:match_spec_test({1, blupp}, ENMS, table)]),
+ ENMSC = try
+ ets:match_spec_compile(ENMS),
+ "compiled"
+ catch
+ error:system_limit ->
+ "got system_limit"
+ end,
+ Tree = ets:new(tree, [ordered_set]),
+ SRT = try
+ ets:select_replace(Tree, ENMS),
+ "compiled"
+ catch
+ error:system_limit ->
+ "got system_limit"
+ end,
+ Hash = ets:new(hash, [set]),
+ SRH = try
+ ets:select_replace(Hash, ENMS),
+ "compiled"
+ catch
+ error:system_limit ->
+ "got system_limit"
+ end,
+ {comment, "match_spec_compile() "++ENMSC++"; select_replace(_,[ordered_set]) "++SRT++"; select_replace(_,[set]) "++SRH}.
%% The following help functions are used by
%% throughput_benchmark. They are declared on the top level beacuse
@@ -7610,32 +8167,73 @@ my_tab_to_list(Ts,Key, Acc) ->
wait_for_memory_deallocations() ->
try
+ erts_debug:set_internal_state(wait, thread_progress),
erts_debug:set_internal_state(wait, deallocations)
catch
error:undef ->
erts_debug:set_internal_state(available_internal_state, true),
- wait_for_memory_deallocations()
+ wait_for_memory_deallocations();
+ error:badarg ->
+ %% The emulator we run on does not have the wait internal state
+ %% so we just sleep some time instead...
+ timer:sleep(100)
end.
etsmem() ->
- % The following is done twice to avoid an inconsistent memory
- % "snapshot" (see verify_etsmem/2).
- lists:foldl(
- fun(_,_) ->
- wait_for_memory_deallocations(),
-
- 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()),
-
- EtsAllocSize = erts_debug:alloc_blocks_size(ets_alloc),
- ErlangMemoryEts = try erlang:memory(ets) catch error:notsup -> notsup end,
-
- Mem = {ErlangMemoryEts, EtsAllocSize},
- {Mem, AllTabs}
- end,
- not_used,
- lists:seq(1,2)).
+ etsmem(get_etsmem(), 1).
+
+etsmem(PrevEtsMem, Try) when Try < 10 ->
+ case get_etsmem() of
+ PrevEtsMem ->
+ PrevEtsMem;
+ EtsMem ->
+ io:format("etsmem(): Change in attempt ~p~n~nbefore:~n~p~n~nafter:~n~p~n~n",
+ [Try, PrevEtsMem, EtsMem]),
+ etsmem(EtsMem, Try+1)
+ end;
+etsmem(_, _) ->
+ ct:fail("Failed to get a stable/consistent memory snapshot").
+
+get_etsmem() ->
+ AllTabsExceptions = [logger, code],
+ %% The logger table is excluded from the AllTabs list
+ %% below because it uses decentralized counters to keep
+ %% track of the size and the memory counters. This cause
+ %% ets:info(T,size) and ets:info(T,memory) to trigger
+ %% allocations and frees that may change the amount of
+ %% memory that is allocated for ETS.
+ %%
+ %% The code table is excluded from the list below
+ %% because the amount of memory allocated for it may
+ %% change if the tested code loads a new module.
+ AllTabs =
+ lists:sort(
+ [begin
+ try ets:info(T, decentralized_counters) of
+ true ->
+ ct:fail("Background ETS table (~p) that "
+ "uses decentralized counters (Add exception?)",
+ [ets:info(T,name)]);
+ _ -> ok
+ catch _:_ ->
+ ok
+ end,
+ {T,
+ ets:info(T,name),
+ ets:info(T,size),
+ ets:info(T,memory),
+ ets:info(T,type)}
+ end
+ || T <- ets:all(),
+ not lists:member(ets:info(T, name), AllTabsExceptions)]),
+ wait_for_memory_deallocations(),
+ EtsAllocSize = erts_debug:alloc_blocks_size(ets_alloc),
+ ErlangMemoryEts = try erlang:memory(ets)
+ catch error:notsup -> notsup end,
+ FlxCtrMemUsage = try erts_debug:get_internal_state(flxctr_memory_usage)
+ catch error:badarg -> notsup end,
+ Mem = {ErlangMemoryEts, EtsAllocSize, FlxCtrMemUsage},
+ {Mem, AllTabs}.
verify_etsmem(MI) ->
wait_for_test_procs(),
diff --git a/lib/stdlib/test/ets_SUITE_data/visualize_throughput.html b/lib/stdlib/test/ets_SUITE_data/visualize_throughput.html
index 6ba00b019a..922a3790ea 100644
--- a/lib/stdlib/test/ets_SUITE_data/visualize_throughput.html
+++ b/lib/stdlib/test/ets_SUITE_data/visualize_throughput.html
@@ -44,6 +44,12 @@
<br>
<textarea id="dataField" rows="4" cols="50">#bench_data_placeholder</textarea>
<br>
+ <input type="checkbox" id="throughputPlot" checked> Include Throughput Plot
+ <br>
+ <input type="checkbox" id="betterThanWorstPlot"> Include % More Throughput Than Worst Plot
+ <br>
+ <input type="checkbox" id="worseThanBestPlot"> Include % Less Throughput Than Best Plot
+ <br>
<input type="checkbox" id="barPlot"> Bar Plot
<br>
<input type="checkbox" id="sameSpacing" checked> Same X Spacing Between Points
@@ -148,10 +154,52 @@
}
return data;
}
+ function toCompareData(dataParam, compareWithWorst) {
+ var data = $.extend(true, [], dataParam);
+ var worstSoFarMap = {};
+ var defaultSoFarValue = compareWithWorst ? Number.MAX_VALUE : Number.MIN_VALUE;
+ function getWorstBestSoFar(x){
+ return worstSoFarMap[x] === undefined ? defaultSoFarValue : worstSoFarMap[x];
+ }
+ function setWorstBestSoFar(x, y){
+ return worstSoFarMap[x] = y;
+ }
+ function lessOrGreaterThan(n1, n2){
+ return compareWithWorst ? n1 < n2 : n1 > n2;
+ }
+ $.each(data, function(i, allResConfig) {
+ $.each(allResConfig.y, function(index, res) {
+ var xName = allResConfig.x[index];
+ if(lessOrGreaterThan(res, getWorstBestSoFar(xName))){
+ setWorstBestSoFar(xName, res);
+ }
+ });
+ });
+ $.each(data, function(i, allResConfig) {
+ $.each(allResConfig.y, function(index, res) {
+ var xName = allResConfig.x[index];
+ if(compareWithWorst){
+ allResConfig.y[index] = ((res / getWorstBestSoFar(xName))-1.0) * 100;
+ }else{
+ allResConfig.y[index] = (1.0 -(res / getWorstBestSoFar(xName))) * 100;
+ }
+ });
+ });
+ return data;
+ }
+ function toBetterThanWorstData(data){
+ return toCompareData(data, true);
+ }
+ function toWorseThanBestData(data){
+ return toCompareData(data, false);
+ }
function plotGraphs(){
var insertPlaceholder = $("#insertPlaceholder");
var sameSpacing = $('#sameSpacing').is(":checked");
var barPlot = $('#barPlot').is(":checked");
+ var throughputPlot = $('#throughputPlot').is(":checked");
+ var betterThanWorstPlot = $('#betterThanWorstPlot').is(":checked");
+ var worseThanBestPlot = $('#worseThanBestPlot').is(":checked");
var lines = $("#dataField").val();
$('.showCheck').each(function() {
var item = $(this);
@@ -188,42 +236,59 @@
plotGraph(lines, sameSpacing, barPlot, prefix));
}
}
+ var nrOfGraphs = 0;
+ function plotScenario(name, plotType) {
+ var data = scenarioDataMap[name];
+ var yAxisTitle = undefined;
+ nrOfGraphs = nrOfGraphs + 1;
+ $("<div class='added' id='graph" + nrOfGraphs + "'>")
+ .insertBefore(insertPlaceholder);
+ $("<button type='button' class='added' id='fullscreenButton" + nrOfGraphs + "'>Fill screen</button>")
+ .insertBefore(insertPlaceholder);
+ $("<span class='added'><br><hr><br></span>")
+ .insertBefore(insertPlaceholder);
+ if (plotType === 'throughput') {
+ yAxisTitle = 'Operations/Second';
+ } else if (plotType === 'better_than_worst') {
+ yAxisTitle = '% More Throughput Than Worst';
+ data = toBetterThanWorstData(data);
+ } else {
+ yAxisTitle = '% Less Throughput Than Best';
+ data = toWorseThanBestData(data);
+ }
+ var layout = {
+ title: name,
+ xaxis: {
+ title: '# of Processes'
+ },
+ yaxis: {
+ title: yAxisTitle
+ }
+ };
+ $("#fullscreenButton" + nrOfGraphs).click(
+ function () {
+ $('#graph' + nrOfGraphs).replaceWith(
+ $("<div class='added' id='graph" + nrOfGraphs + "'>"));
+ layout = $.extend({}, layout, {
+ width: $(window).width() - 40,
+ height: $(window).height() - 40
+ });
+ Plotly.newPlot('graph' + nrOfGraphs, data, layout);
+ });
+ Plotly.newPlot('graph' + nrOfGraphs, data, layout);
+ }
$.each(scenarioList,
- function( index, name ) {
- var nrOfGraphs = index + 1;
- var data = scenarioDataMap[name];
- $( "<div class='added' id='graph"+nrOfGraphs+"'>")
- .insertBefore( insertPlaceholder );
- $( "<button type='button' class='added' id='fullscreenButton"+nrOfGraphs+"'>Fill screen</button>")
- .insertBefore( insertPlaceholder );
- $( "<span class='added'><br><hr><br></span>")
- .insertBefore( insertPlaceholder );
- var layout = {
- title:name,
- xaxis: {
- title: '# of Processes'
- },
- yaxis: {
- title: 'Operations/Second'
- }
-
- };
-
- $("#fullscreenButton"+nrOfGraphs).click(
- function(){
- $('#graph'+nrOfGraphs).replaceWith(
- $("<div class='added' id='graph"+nrOfGraphs+"'>"));
- layout = $.extend({}, layout, {
- width:$(window).width()-40,
- height:$(window).height()-40
- });
- Plotly.newPlot('graph'+nrOfGraphs, data, layout);
- });
- Plotly.newPlot('graph'+nrOfGraphs, data, layout);
-
- });
-
-
+ function (index, name) {
+ if (throughputPlot) {
+ plotScenario(name, 'throughput');
+ }
+ if (betterThanWorstPlot) {
+ plotScenario(name, 'better_than_worst');
+ }
+ if (worseThanBestPlot) {
+ plotScenario(name, 'worse_than_best');
+ }
+ });
}
$(document).ready(function(){
$('#renderButton').click(
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index ddb3a3d767..203ba2c31e 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -26,7 +26,8 @@
wildcard_one/1,wildcard_two/1,wildcard_errors/1,
fold_files/1,otp_5960/1,ensure_dir_eexist/1,ensure_dir_symlink/1,
wildcard_symlink/1, is_file_symlink/1, file_props_symlink/1,
- find_source/1, find_source_subdir/1]).
+ find_source/1, find_source_subdir/1, safe_relative_path/1,
+ safe_relative_path_links/1]).
-import(lists, [foreach/2]).
@@ -49,7 +50,8 @@ all() ->
[wildcard_one, wildcard_two, wildcard_errors,
fold_files, otp_5960, ensure_dir_eexist, ensure_dir_symlink,
wildcard_symlink, is_file_symlink, file_props_symlink,
- find_source, find_source_subdir].
+ find_source, find_source_subdir, safe_relative_path,
+ safe_relative_path_links].
groups() ->
[].
@@ -121,6 +123,8 @@ wcc(Wc, Error) ->
{'EXIT',{{badpattern,Error},
[{filelib,wildcard,2,_}|_]}} = (catch filelib:wildcard(Wc, ".")).
+disable_prefix_opt([_,$:|_]=Wc) ->
+ Wc;
disable_prefix_opt([C|Wc]) when $a =< C, C =< $z; C =:= $@ ->
%% There is an optimization for patterns that have a literal prefix
%% (such as "lib/compiler/ebin/*"). Test that we'll get the same result
@@ -647,3 +651,167 @@ find_source_subdir(Config) when is_list(Config) ->
{ok, SrcFile} = filelib:find_file(SrcName, BeamDir),
ok.
+
+safe_relative_path(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Root = filename:join(PrivDir, "filelib_SUITE_safe_relative_path"),
+ ok = file:make_dir(Root),
+ ok = file:set_cwd(Root),
+
+ ok = file:make_dir("a"),
+ ok = file:set_cwd("a"),
+ ok = file:make_dir("b"),
+ ok = file:set_cwd("b"),
+ ok = file:make_dir("c"),
+
+ ok = file:set_cwd(Root),
+
+ "a" = test_srp("a"),
+ "a/b" = test_srp("a/b"),
+ "a/b" = test_srp("a/./b"),
+ "a/b" = test_srp("a/./b/."),
+
+ "" = test_srp("a/.."),
+ "" = test_srp("a/./.."),
+ "" = test_srp("a/../."),
+ "a" = test_srp("a/b/.."),
+ "a" = test_srp("a/../a"),
+ "a" = test_srp("a/../a/../a"),
+ "a/b/c" = test_srp("a/../a/b/c"),
+
+ unsafe = test_srp("a/../.."),
+ unsafe = test_srp("a/../../.."),
+ unsafe = test_srp("a/./../.."),
+ unsafe = test_srp("a/././../../.."),
+ unsafe = test_srp("a/b/././../../.."),
+
+ unsafe = test_srp(PrivDir), %Absolute path.
+
+ ok.
+
+test_srp(RelPath) ->
+ Res = do_test_srp(RelPath),
+ Res = case do_test_srp(list_to_binary(RelPath)) of
+ Bin when is_binary(Bin) ->
+ binary_to_list(Bin);
+ Other ->
+ Other
+ end.
+
+do_test_srp(RelPath) ->
+ {ok,Root} = file:get_cwd(),
+ ok = file:set_cwd(RelPath),
+ {ok,Cwd} = file:get_cwd(),
+ ok = file:set_cwd(Root),
+ case filelib:safe_relative_path(RelPath, Cwd) of
+ unsafe ->
+ true = length(Cwd) < length(Root),
+ unsafe;
+ "" ->
+ "";
+ SafeRelPath ->
+ ok = file:set_cwd(SafeRelPath),
+ {ok,Cwd} = file:get_cwd(),
+ true = length(Cwd) >= length(Root),
+ ok = file:set_cwd(Root),
+ SafeRelPath
+ end.
+
+safe_relative_path_links(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = filename:join(PrivDir, "filelib_SUITE_safe_relative_path_links"),
+ ok = file:make_dir(BaseDir),
+ try
+ case check_symlink_support(BaseDir) of
+ true ->
+ simple_test(BaseDir),
+ inside_directory_test(BaseDir),
+ nested_links_test(BaseDir),
+ loop_test(BaseDir),
+ loop_with_parent_test(BaseDir),
+ revist_links_test(BaseDir);
+ false ->
+ {skipped, "This platform/user can't create symlinks."}
+ end
+ after
+ %% This test leaves some rather nasty links that may screw with
+ %% z_SUITE's core file search, so we must make sure everything's
+ %% removed regardless of what happens.
+ rm_rf(BaseDir)
+ end.
+
+check_symlink_support(BaseDir) ->
+ Canary = filename:join(BaseDir, "symlink_canary"),
+ Link = filename:join(BaseDir, "symlink_canary_link"),
+ ok = file:write_file(Canary, <<"chirp">>),
+ ok =:= file:make_symlink(Canary, Link).
+
+simple_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "simple_test")),
+ file:make_symlink("..", filename:join(BaseDir, "simple_test/link")),
+
+ unsafe = filelib:safe_relative_path("link/file", filename:join(BaseDir, "simple_test")),
+ "file" = filelib:safe_relative_path("file", filename:join(BaseDir, "simple_test/link")).
+
+inside_directory_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "inside_directory_test")),
+ file:make_symlink("..", filename:join(BaseDir, "inside_directory_test/link")),
+
+ unsafe = filelib:safe_relative_path("link/file", filename:join(BaseDir, "inside_directory_test")),
+ "file" = filelib:safe_relative_path("file", filename:join(BaseDir, "inside_directory_test/link")).
+
+nested_links_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "nested_links_test")),
+ file:make_dir(filename:join(BaseDir, "nested_links_test/a")),
+ file:make_symlink("a/b/c", filename:join(BaseDir, "nested_links_test/link")),
+ file:make_symlink("..", filename:join(BaseDir, "nested_links_test/a/b")),
+
+ "c/file" = filelib:safe_relative_path("link/file", filename:join(BaseDir, "nested_links_test")),
+
+ file:delete(filename:join(BaseDir, "nested_links_test/a/b")),
+ file:make_symlink("../..", filename:join(BaseDir, "nested_links_test/a/b")),
+ unsafe = filelib:safe_relative_path("link/file", filename:join(BaseDir, "nested_links_test")).
+
+loop_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "loop_test")),
+
+ file:make_symlink("b", filename:join(BaseDir, "loop_test/c")),
+ file:make_symlink("c", filename:join(BaseDir, "loop_test/b")),
+
+ unsafe = filelib:safe_relative_path("c", filename:join(BaseDir, "loop_test")).
+
+loop_with_parent_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "loop_with_parent_test")),
+ file:make_dir(filename:join(BaseDir, "loop_with_parent_test/bar")),
+
+ file:make_symlink("../bar/foo", filename:join(BaseDir, "loop_with_parent_test/bar/foo")),
+
+ unsafe = filelib:safe_relative_path("bar/foo", filename:join(BaseDir, "loop_with_parent_test")).
+
+revist_links_test(BaseDir) ->
+ file:make_dir(filename:join(BaseDir, "revist_links_test")),
+
+ file:make_symlink(".", filename:join(BaseDir, "revist_links_test/x")),
+ file:make_symlink("x", filename:join(BaseDir, "revist_links_test/y")),
+ file:make_symlink("y", filename:join(BaseDir, "revist_links_test/z")),
+
+ "file" = filelib:safe_relative_path("x/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("y/x/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/x/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/y/x/y/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/y/z/x/y/z/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/x/y/y/file", filename:join(BaseDir, "revist_links_test")),
+ "file" = filelib:safe_relative_path("x/z/y/x/./z/foo/../x/./y/file", filename:join(BaseDir, "revist_links_test")).
+
+rm_rf(Dir) ->
+ case file:read_link_info(Dir) of
+ {ok, #file_info{type = directory}} ->
+ {ok, Content} = file:list_dir_all(Dir),
+ [ rm_rf(filename:join(Dir,C)) || C <- Content ],
+ file:del_dir(Dir),
+ ok;
+ {ok, #file_info{}} ->
+ file:delete(Dir);
+ _ ->
+ ok
+ end.
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index f284eb1ed6..846394d366 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -886,7 +886,7 @@ t_nativename_bin(Config) when is_list(Config) ->
safe_relative_path(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
- Root = filename:join(PrivDir, ?FUNCTION_NAME),
+ Root = filename:join(PrivDir, "filename_SUITE_safe_relative_path"),
ok = file:make_dir(Root),
ok = file:set_cwd(Root),
@@ -1081,7 +1081,10 @@ check_basedir_xdg([Type|Types]) ->
Opt = #{os=>linux},
Key = basedir_xdg_env(Type),
io:format("type: ~p~n", [Type]),
- Home = os:getenv("HOME"),
+ Home = case os:getenv("WSLENV") of
+ false -> os:getenv("HOME");
+ _ -> os:getenv("USERPROFILE")
+ end,
NDir = "/some/absolute/path",
DefPath = basedir_xdg_def(Type,Home,Name),
EnvPath = case Type of
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 880b10117c..cb292cf01f 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-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@
start_opt/1,
undef_init/1, undef_handle_call/1, undef_handle_event/1,
undef_handle_info/1, undef_code_change/1, undef_terminate/1,
- undef_in_terminate/1]).
+ undef_in_terminate/1, format_log_1/1, format_log_2/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -40,7 +40,8 @@ all() ->
[start, {group, test_all}, hibernate, auto_hibernate,
call_format_status, call_format_status_anon, error_format_status,
get_state, replace_state,
- start_opt, {group, undef_callbacks}, undef_in_terminate].
+ start_opt, {group, undef_callbacks}, undef_in_terminate,
+ format_log_1, format_log_2].
groups() ->
[{test_all, [],
@@ -112,6 +113,11 @@ start(Config) when is_list(Config) ->
[] = gen_event:which_handlers(Pid1),
ok = gen_event:stop(Pid1),
+ {ok, {Pid1b,Mon1b}} = gen_event:start_monitor(), %anonymous
+ [] = gen_event:which_handlers(Pid1b),
+ ok = gen_event:stop(Pid1b),
+ receive {'DOWN',Mon1b,process,Pid1b,_} -> ok end,
+
{ok, Pid2} = gen_event:start(?LMGR),
[] = gen_event:which_handlers(my_dummy_name),
[] = gen_event:which_handlers(Pid2),
@@ -122,21 +128,45 @@ start(Config) when is_list(Config) ->
[] = gen_event:which_handlers(Pid3),
ok = gen_event:stop(my_dummy_name),
+ {ok, {Pid3b,Mon3b}} = gen_event:start_monitor(?LMGR),
+ [] = gen_event:which_handlers(my_dummy_name),
+ [] = gen_event:which_handlers(Pid3b),
+ ok = gen_event:stop(my_dummy_name),
+ receive {'DOWN',Mon3b,process,Pid3b,_} -> ok end,
+
{ok, Pid4} = gen_event:start_link(?GMGR),
[] = gen_event:which_handlers(?GMGR),
[] = gen_event:which_handlers(Pid4),
ok = gen_event:stop(?GMGR),
+ {ok, {Pid4b,Mon4b}} = gen_event:start_monitor(?GMGR),
+ [] = gen_event:which_handlers(?GMGR),
+ [] = gen_event:which_handlers(Pid4b),
+ ok = gen_event:stop(?GMGR),
+ receive {'DOWN',Mon4b,process,Pid4b,_} -> ok end,
+
{ok, Pid5} = gen_event:start_link({via, dummy_via, my_dummy_name}),
[] = gen_event:which_handlers({via, dummy_via, my_dummy_name}),
[] = gen_event:which_handlers(Pid5),
ok = gen_event:stop({via, dummy_via, my_dummy_name}),
+ {ok, {Pid5b,Mon5b}} = gen_event:start_monitor({via, dummy_via, my_dummy_name}),
+ [] = gen_event:which_handlers({via, dummy_via, my_dummy_name}),
+ [] = gen_event:which_handlers(Pid5b),
+ ok = gen_event:stop({via, dummy_via, my_dummy_name}),
+ receive {'DOWN',Mon5b,process,Pid5b,_} -> ok end,
+
{ok, _} = gen_event:start_link(?LMGR),
{error, {already_started, _}} = gen_event:start_link(?LMGR),
{error, {already_started, _}} = gen_event:start(?LMGR),
ok = gen_event:stop(my_dummy_name),
+ {ok, {Pid5c,Mon5c}} = gen_event:start_monitor(?LMGR),
+ {error, {already_started, Pid5c}} = gen_event:start_monitor(?LMGR),
+ {error, {already_started, Pid5c}} = gen_event:start(?LMGR),
+ ok = gen_event:stop(my_dummy_name),
+ receive {'DOWN',Mon5c,process,Pid5c,_} -> ok end,
+
{ok, Pid6} = gen_event:start_link(?GMGR),
{error, {already_started, _}} = gen_event:start_link(?GMGR),
{error, {already_started, _}} = gen_event:start(?GMGR),
@@ -148,6 +178,17 @@ start(Config) when is_list(Config) ->
ct:fail(exit_gen_event)
end,
+ {ok, {Pid6b,Mon6b}} = gen_event:start_monitor(?GMGR),
+ {error, {already_started, _}} = gen_event:start_monitor(?GMGR),
+ {error, {already_started, _}} = gen_event:start(?GMGR),
+
+ ok = gen_event:stop(?GMGR, shutdown, 10000),
+ receive
+ {'DOWN', Mon6b, process, Pid6b, shutdown} -> ok
+ after 10000 ->
+ ct:fail(exit_gen_event)
+ end,
+
{ok, Pid7} = gen_event:start_link({via, dummy_via, my_dummy_name}),
{error, {already_started, _}} = gen_event:start_link({via, dummy_via, my_dummy_name}),
{error, {already_started, _}} = gen_event:start({via, dummy_via, my_dummy_name}),
@@ -159,6 +200,17 @@ start(Config) when is_list(Config) ->
ct:fail(exit_gen_event)
end,
+ {ok, {Pid7b,Mon7b}} = gen_event:start_monitor({via, dummy_via, my_dummy_name}),
+ {error, {already_started, _}} = gen_event:start_monitor({via, dummy_via, my_dummy_name}),
+ {error, {already_started, _}} = gen_event:start({via, dummy_via, my_dummy_name}),
+
+ exit(Pid7b, shutdown),
+ receive
+ {'DOWN', Mon7b, process, Pid7b, shutdown} -> ok
+ after 10000 ->
+ ct:fail(exit_gen_event)
+ end,
+
process_flag(trap_exit, OldFl),
ok.
@@ -763,27 +815,49 @@ sync_notify(Config) when is_list(Config) ->
ok.
call(Config) when is_list(Config) ->
+ Async = fun(Mgr,H,Req) ->
+ try
+ Promise = gen_event:send_request(Mgr,H,Req),
+ gen_event:wait_response(Promise, infinity)
+ catch _:Reason ->
+ {'did_exit', Reason}
+ end
+ end,
{ok,_} = gen_event:start({local, my_dummy_handler}),
ok = gen_event:add_handler(my_dummy_handler, dummy_h, [self()]),
ok = gen_event:add_handler(my_dummy_handler, {dummy_h, 1}, [self()]),
[{dummy_h, 1}, dummy_h] = gen_event:which_handlers(my_dummy_handler),
{'EXIT',_} = (catch gen_event:call(non_exist, dummy_h, hejsan)),
- {error, bad_module} =
- gen_event:call(my_dummy_handler, bad_h, hejsan),
+ {error, _} = Async(non_exist, dummy_h, hejsan),
+ {error, bad_module} = gen_event:call(my_dummy_handler, bad_h, hejsan),
+ {error, bad_module} = Async(my_dummy_handler, bad_h, hejsan),
+
{ok, hejhopp} = gen_event:call(my_dummy_handler, dummy_h, hejsan),
- {ok, hejhopp} = gen_event:call(my_dummy_handler, {dummy_h, 1},
- hejsan),
- {ok, hejhopp} = gen_event:call(my_dummy_handler, dummy_h, hejsan,
- 10000),
+ {reply, {ok, hejhopp}} = Async(my_dummy_handler, dummy_h, hejsan),
+ {ok, hejhopp} = gen_event:call(my_dummy_handler, {dummy_h, 1}, hejsan),
+ {reply, {ok, hejhopp}} = Async(my_dummy_handler, {dummy_h, 1}, hejsan),
+ {ok, hejhopp} = gen_event:call(my_dummy_handler, {dummy_h, 1}, hejsan),
+ {reply, {ok, hejhopp}} = Async(my_dummy_handler, {dummy_h, 1}, hejsan),
+ {ok, hejhopp} = gen_event:call(my_dummy_handler, dummy_h, hejsan, 10000),
{'EXIT', {timeout, _}} =
(catch gen_event:call(my_dummy_handler, dummy_h, hejsan, 0)),
flush(),
+ P1 = gen_event:send_request(my_dummy_handler, dummy_h, hejsan),
+ timeout = gen_event:wait_response(P1, 0),
+ {reply, {ok, hejhopp}} = gen_event:wait_response(P1, infinity),
+
+ flush(),
+ P2 = gen_event:send_request(my_dummy_handler, dummy_h, hejsan),
+ no_reply = gen_event:check_response({other,msg}, P2),
+ {reply, {ok, hejhopp}} = receive Msg -> gen_event:check_response(Msg, P2)
+ after 1000 -> exit(tmo) end,
+
ok = gen_event:delete_handler(my_dummy_handler, {dummy_h, 1}, []),
{ok, swapped} = gen_event:call(my_dummy_handler, dummy_h,
{swap_call,dummy1_h,swap}),
[dummy1_h] = gen_event:which_handlers(my_dummy_handler),
- {error, bad_module} =
- gen_event:call(my_dummy_handler, dummy_h, hejsan),
+ {error, bad_module} = gen_event:call(my_dummy_handler, dummy_h, hejsan),
+ {error, bad_module} = Async(my_dummy_handler, dummy_h, hejsan),
ok = gen_event:call(my_dummy_handler, dummy1_h, delete_call),
receive
{dummy1_h, removed} ->
@@ -1221,3 +1295,209 @@ fake_upgrade(Pid, Mod) ->
Ret = sys:change_code(Pid, Mod, old_vsn, []),
ok = sys:resume(Pid),
Ret.
+
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1, 15),
+ Handler = my_handler,
+ Name = self(),
+ Report = #{label=>{gen_event,terminate},
+ handler=>Handler,
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ reason=>Term},
+ {F1, A1} = gen_event:format_log(Report),
+ FExpected1 = "** gen_event handler ~tp crashed.\n"
+ "** Was installed in ~tp\n"
+ "** Last event was: ~tp\n"
+ "** When handler state == ~tp\n"
+ "** Reason == ~tp\n",
+ ct:log("F1: ~ts~nA1: ~tp", [F1,A1]),
+ FExpected1 = F1,
+ [Handler,Name,Term,Term,Term] = A1,
+
+ Warning = #{label=>{gen_event,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ {WF1,WA1} = gen_event:format_log(Warning),
+ WFExpected1 = "** Undefined handle_info in ~p\n"
+ "** Unhandled message: ~tp\n",
+ ct:log("WF1: ~ts~nWA1: ~tp", [WF1,WA1]),
+ WFExpected1 = WF1,
+ [?MODULE,Term] = WA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = gen_event:format_log(#{label=>{gen_event,terminate},
+ handler=>Handler,
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ reason=>Term}),
+ FExpected2 = "** gen_event handler ~tP crashed.\n"
+ "** Was installed in ~tP\n"
+ "** Last event was: ~tP\n"
+ "** When handler state == ~tP\n"
+ "** Reason == ~tP\n",
+ ct:log("F2: ~ts~nA2: ~tp", [F2,A2]),
+ FExpected2 = F2,
+ [Handler,Depth,Name,Depth,Limited,Depth,Limited,Depth,Limited,Depth] = A2,
+
+ {WF2,WA2} = gen_event:format_log(Warning),
+ WFExpected2 = "** Undefined handle_info in ~p\n"
+ "** Unhandled message: ~tP\n",
+ ct:log("WF2: ~ts~nWA2: ~tp", [WF2,WA2]),
+ WFExpected2 = WF2,
+ [?MODULE,Limited,Depth] = WA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1, 15),
+ Handler = my_handler,
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Report = #{label=>{gen_event,terminate},
+ handler=>Handler,
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ reason=>Term},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** gen_event handler my_handler crashed.\n"
+ "** Was installed in "++NameStr++"\n"
+ "** Last event was: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** When handler state == [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** Reason == [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = Expected1 =:= Str1,
+
+ Warning = #{label=>{gen_event,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ WStr1 = flatten_format_log(Warning, FormatOpts1),
+ WL1 = length(WStr1),
+ WExpected1 = "** Undefined handle_info in gen_event_SUITE\n"
+ "** Unhandled message: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("WStr1: ~ts", [WStr1]),
+ ct:log("length(WStr1): ~p", [WL1]),
+ true = WExpected1 =:= WStr1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** gen_event handler my_handler crashed.\n"
+ "** Was installed in " ++ NameStr ++ "\n"
+ "** Last event was: [1,2,3,4,5,6,7,8,9|...]\n"
+ "** When handler state == [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Reason == [1,2,3,4,5,6,7,8,9|...]\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ WStr2 = flatten_format_log(Warning, FormatOpts2),
+ WL2 = length(WStr2),
+ WExpected2 = "** Undefined handle_info in gen_event_SUITE\n"
+ "** Unhandled message: [1,2,3,4,5,6,7,8,9|...]\n",
+ ct:log("WStr2: ~ts", [WStr2]),
+ ct:log("length(WStr2): ~p", [WL2]),
+ true = WExpected2 =:= WStr2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** gen_event handler my_handler crashed.\n"
+ "** Was installed",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ WFormatOpts3 = #{chars_limit=>80},
+ WStr3 = flatten_format_log(Warning, WFormatOpts3),
+ WL3 = length(WStr3),
+ WExpected3 = "** Undefined handle_info in gen_event_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr3: ~ts", [WStr3]),
+ ct:log("length(WStr3): ~p", [WL3]),
+ true = lists:prefix(WExpected3, WStr3),
+ true = WL3 < WL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+
+ Expected4 = "Generic event handler my_handler crashed. "
+ "Installed: "++NameStr++". "
+ "Last event: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "State: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ true = Expected4 =:= Str4,
+
+ WStr4 = flatten_format_log(Warning, FormatOpts4),
+ WL4 = length(WStr4),
+ WExpected4 = "Undefined handle_info in gen_event_SUITE. "
+ "Unhandled message: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("WStr4: ~ts", [WStr4]),
+ ct:log("length(WStr4): ~p", [WL4]),
+ true = WExpected4 =:= WStr4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Generic event handler my_handler crashed. "
+ "Installed: "++NameStr++". "
+ "Last event: [1,2,3,4,5,6,7,8,9|...]. "
+ "State: [1,2,3,4,5,6,7,8,9|...]. "
+ "Reason: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ true = Expected5 =:= Str5,
+
+ WStr5 = flatten_format_log(Warning, FormatOpts5),
+ WL5 = length(WStr5),
+ WExpected5 = "Undefined handle_info in gen_event_SUITE. "
+ "Unhandled message: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("WStr5: ~ts", [WStr5]),
+ ct:log("length(WStr5): ~p", [WL5]),
+ true = WExpected5 =:= WStr5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>200},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Generic event handler my_handler crashed. Installed: ",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ WFormatOpts6 = #{single_line=>true, chars_limit=>80},
+ WStr6 = flatten_format_log(Warning, WFormatOpts6),
+ WL6 = length(WStr6),
+ WExpected6 = "Undefined handle_info in gen_event_SUITE. ",
+ ct:log("WStr6: ~ts", [WStr6]),
+ ct:log("length(WStr6): ~p", [WL6]),
+ true = lists:prefix(WExpected6, WStr6),
+ true = WL6 < WL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(gen_event:format_log(Report, Format)).
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index 62d9d0e0ae..6840184c74 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -46,6 +46,8 @@
-export([hibernate/1,auto_hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]).
+-export([format_log_1/1, format_log_2/1]).
+
-export([enter_loop/1]).
%% Exports for apply
@@ -69,7 +71,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group, start}, {group, abnormal}, shutdown,
{group, sys}, hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
- undef_in_handle_info, undef_in_terminate].
+ undef_in_handle_info, undef_in_terminate,{group,format_log}].
groups() ->
[{start, [],
@@ -83,7 +85,8 @@ groups() ->
get_state, replace_state]},
{undef_callbacks, [],
[undef_handle_event, undef_handle_sync_event, undef_handle_info,
- undef_init, undef_code_change, undef_terminate1, undef_terminate2]}].
+ undef_init, undef_code_change, undef_terminate1, undef_terminate2]},
+ {format_log, [], [format_log_1, format_log_2]}].
init_per_suite(Config) ->
Config.
@@ -1018,6 +1021,236 @@ undef_in_terminate(Config) when is_list(Config) ->
ok
end.
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1, 15),
+ Name = self(),
+ Report = #{label=>{gen_fsm,terminate},
+ name=>Name,
+ last_message=>Term,
+ state_name=>Name,
+ state_data=>Term,
+ log=>[Term],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ {F1,A1} = gen_fsm:format_log(Report),
+ FExpected1 = "** State machine ~tp terminating \n"
+ "** Last message in was ~tp~n"
+ "** When State == ~tp~n"
+ "** Data == ~tp~n"
+ "** Reason for termination ==~n** ~tp~n"
+ "** Log ==~n**~tp~n"
+ "** Client ~tp stacktrace~n** ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp", [F1,A1]),
+ FExpected1=F1,
+
+ [Name,Term,Name,Term,Term,[Term],clientname,[]] = A1,
+
+ Warning = #{label=>{gen_fsm,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ {WF1,WA1} = gen_fsm:format_log(Warning),
+ WFExpected1 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tp~n",
+ ct:log("WF1: ~ts~nWA1: ~tp", [WF1,WA1]),
+ WFExpected1=WF1,
+ [?MODULE,Term] = WA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = gen_fsm:format_log(#{label=>{gen_fsm,terminate},
+ name=>Name,
+ last_message=>Term,
+ state_name=>Name,
+ state_data=>Term,
+ log=>[Term],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}}),
+ FExpected2 = "** State machine ~tP terminating \n"
+ "** Last message in was ~tP~n"
+ "** When State == ~tP~n"
+ "** Data == ~tP~n"
+ "** Reason for termination ==~n** ~tP~n"
+ "** Log ==~n**~tP~n"
+ "** Client ~tP stacktrace~n** ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp", [F2,A2]),
+ FExpected2=F2,
+
+ [Name,Depth,Limited,Depth,Name,Depth,Limited,Depth,Limited,
+ Depth,[Limited],Depth,clientname,Depth,[],Depth] = A2,
+
+ {WF2,WA2} = gen_fsm:format_log(Warning),
+ WFExpected2 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tP~n",
+ ct:log("WF2: ~ts~nWA2: ~tp", [WF2,WA2]),
+ WFExpected2=WF2,
+ [?MODULE,Limited,Depth] = WA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1, 15),
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Report = #{label=>{gen_fsm,terminate},
+ name=>Name,
+ last_message=>Term,
+ state_name=>Name,
+ state_data=>Term,
+ log=>[Term],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** State machine "++NameStr++" terminating \n"
+ "** Last message in was [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** When State == "++NameStr++"\n"
+ "** Data == [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** Reason for termination ==\n"
+ "** [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** Log ==\n"
+ "**[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]\n"
+ "** Client clientname stacktrace\n"
+ "** []\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = Expected1 =:= Str1,
+
+ Warning = #{label=>{gen_fsm,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ WStr1 = flatten_format_log(Warning, FormatOpts1),
+ WL1 = length(WStr1),
+ WExpected1 = "** Undefined handle_info in gen_fsm_SUITE\n"
+ "** Unhandled message: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("WStr1: ~ts", [WStr1]),
+ ct:log("length(WStr1): ~p", [WL1]),
+ true = WExpected1 =:= WStr1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** State machine "++NameStr++" terminating \n"
+ "** Last message in was [1,2,3,4,5,6,7,8,9|...]\n"
+ "** When State == "++NameStr++"\n"
+ "** Data == [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Reason for termination ==\n"
+ "** [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Log ==\n"
+ "**[[1,2,3,4,5,6,7,8|...]]\n"
+ "** Client clientname stacktrace\n"
+ "** []\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ WStr2 = flatten_format_log(Warning, FormatOpts2),
+ WL2 = length(WStr2),
+ WExpected2 = "** Undefined handle_info in gen_fsm_SUITE\n"
+ "** Unhandled message: [1,2,3,4,5,6,7,8,9|...]\n",
+ ct:log("WStr2: ~ts", [WStr2]),
+ ct:log("length(WStr2): ~p", [WL2]),
+ true = WExpected2 =:= WStr2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** State machine "++NameStr++" terminating \n"
+ "** Last ",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ WFormatOpts3 = #{chars_limit=>80},
+ WStr3 = flatten_format_log(Warning, WFormatOpts3),
+ WL3 = length(WStr3),
+ WExpected3 = "** Undefined handle_info in gen_fsm_SUITE",
+ ct:log("WStr3: ~ts", [WStr3]),
+ ct:log("length(WStr3): ~p", [WL3]),
+ true = lists:prefix(WExpected3, WStr3),
+ true = WL3 < WL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "State machine "++NameStr++" terminating. "
+ "Reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Last event: [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]. "
+ "State: "++NameStr++". "
+ "Data: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Log: [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]. "
+ "Client clientname stacktrace: [].",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ true = Expected4 =:= Str4,
+
+ WStr4 = flatten_format_log(Warning, FormatOpts4),
+ WL4 = length(WStr4),
+ WExpected4 = "Undefined handle_info in gen_fsm_SUITE. "
+ "Unhandled message: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("WStr4: ~ts", [WStr4]),
+ ct:log("length(WStr4): ~p", [WL4]),
+ true = WExpected4 =:= WStr4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "State machine "++NameStr++" terminating. "
+ "Reason: [1,2,3,4,5,6,7,8,9|...]. "
+ "Last event: [[1,2,3,4,5,6,7,8|...]]. "
+ "State: "++NameStr++". "
+ "Data: [1,2,3,4,5,6,7,8,9|...]. "
+ "Log: [[1,2,3,4,5,6,7,8|...]]. "
+ "Client clientname stacktrace: [].",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ true = Expected5 =:= Str5,
+
+ WStr5 = flatten_format_log(Warning, FormatOpts5),
+ WL5 = length(WStr5),
+ WExpected5 = "Undefined handle_info in gen_fsm_SUITE. "
+ "Unhandled message: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("WStr5: ~ts", [WStr5]),
+ ct:log("length(WStr5): ~p", [WL5]),
+ true = WExpected5 =:= WStr5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>200},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "State machine "++NameStr++" terminating. Reason: ",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ WFormatOpts6 = #{single_line=>true, chars_limit=>80},
+ WStr6 = flatten_format_log(Warning, WFormatOpts6),
+ WL6 = length(WStr6),
+ WExpected6 = "Undefined handle_info in gen_fsm_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr6: ~ts", [WStr6]),
+ ct:log("length(WStr6): ~p", [WL6]),
+ true = lists:prefix(WExpected6, WStr6),
+ true = WL6 < WL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(gen_fsm:format_log(Report, Format)).
+
%%
%% Functionality check
%%
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index e29195e895..8015126b0d 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -26,7 +26,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([start/1, crash/1, call/1, cast/1, cast_fast/1,
+-export([start/1, crash/1, call/1, send_request/1, cast/1, cast_fast/1,
continue/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,
@@ -38,7 +38,9 @@
undef_handle_call/1, undef_handle_cast/1, undef_handle_info/1,
undef_init/1, undef_code_change/1, undef_terminate1/1,
undef_terminate2/1, undef_in_terminate/1, undef_in_handle_info/1,
- undef_handle_continue/1
+ undef_handle_continue/1,
+
+ format_log_1/1, format_log_2/1
]).
-export([stop1/1, stop2/1, stop3/1, stop4/1, stop5/1, stop6/1, stop7/1,
@@ -61,7 +63,7 @@ suite() ->
{timetrap,{minutes,1}}].
all() ->
- [start, {group,stop}, crash, call, cast, cast_fast, info, abcast,
+ [start, {group,stop}, crash, call, send_request, cast, cast_fast, info, abcast,
continue, multicall, multicall_down, call_remote1, call_remote2,
call_remote3, call_remote_n1, call_remote_n2,
call_remote_n3, spec_init,
@@ -71,7 +73,8 @@ all() ->
call_format_status, error_format_status, terminate_crash_format,
get_state, replace_state,
call_with_huge_message_queue, {group, undef_callbacks},
- undef_in_terminate, undef_in_handle_info].
+ undef_in_terminate, undef_in_handle_info,
+ format_log_1, format_log_2].
groups() ->
[{stop, [],
@@ -104,7 +107,8 @@ init_per_testcase(Case, Config) when Case == call_remote1;
Case == call_remote3;
Case == call_remote_n1;
Case == call_remote_n2;
- Case == call_remote_n3 ->
+ Case == call_remote_n3;
+ Case == send_request ->
{ok,N} = start_node(hubba),
[{node,N} | Config];
@@ -161,6 +165,18 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% anonymous monitored
+ {ok, {Pid1b, Mon1b}} =
+ gen_server:start_monitor(gen_server_SUITE, [], []),
+ ok = gen_server:call(Pid1b, started_p),
+ ok = gen_server:call(Pid1b, stop),
+ receive
+ {'DOWN', Mon1b, process, Pid1b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% local register
{ok, Pid2} =
gen_server:start({local, my_test_name},
@@ -191,6 +207,22 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% local register monitored
+ {ok, {Pid3b, Mon3b}} =
+ gen_server:start_monitor({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call(my_test_name, started_p),
+ {error, {already_started, Pid3b}} =
+ gen_server:start_monitor({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call(my_test_name, stop),
+ receive
+ {'DOWN', Mon3b, process, Pid3b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% global register
{ok, Pid4} =
gen_server:start({global, my_test_name},
@@ -219,6 +251,22 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% global register monitored
+ {ok, {Pid5b, Mon5b}} =
+ gen_server:start_monitor({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call({global, my_test_name}, started_p),
+ {error, {already_started, Pid5b}} =
+ gen_server:start_monitor({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call({global, my_test_name}, stop),
+ receive
+ {'DOWN', Mon5b, process, Pid5b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% via register
dummy_via:reset(),
{ok, Pid6} =
@@ -459,6 +507,90 @@ call(Config) when is_list(Config) ->
ok.
%% --------------------------------------
+%% Test gen_server:send_request.
+%% --------------------------------------
+
+send_request(Config) when is_list(Config) ->
+ OldFl = process_flag(trap_exit, true),
+
+ {ok, Pid} = gen_server:start_link({local, my_test_name},
+ gen_server_SUITE, [], []),
+
+ Async = fun(Process, Req) ->
+ try
+ Promise = gen_server:send_request(Process, Req),
+ gen_server:wait_response(Promise, infinity)
+ catch _:Reason:ST ->
+ {'did_exit', Reason, ST}
+ end
+ end,
+ {reply,ok} = Async(my_test_name, started_p),
+
+ {reply,delayed} = Async(Pid, {delayed_answer,1}),
+
+ %% two requests within a specified time.
+ Promise1 = gen_server:send_request(my_test_name, {call_within, 1000}),
+ Promise2 = gen_server:send_request(my_test_name, next_call),
+ {reply, ok} = gen_server:wait_response(Promise1, infinity),
+ {reply, ok} = gen_server:wait_response(Promise2, infinity),
+
+ Promise3 = gen_server:send_request(my_test_name, {call_within, 1000}),
+ no_reply = gen_server:check_response({foo, bar}, Promise3),
+ receive {{'$gen_request_id',Ref},_} = Msg when is_reference(Ref) ->
+ {reply, ok} = gen_server:check_response(Msg, Promise3)
+ after 1000 ->
+ %% Format not yet doumented so it might be ok
+ %% This test is just to make you aware that you have changed it
+ exit(message_format_changed)
+ end,
+ timer:sleep(1500),
+
+ {reply, false} = Async(my_test_name, next_call),
+
+ %% timeout
+ Promise5 = gen_server:send_request(my_test_name, {delayed_answer,50}),
+ timeout = gen_server:wait_response(Promise5, 0),
+ {reply, delayed} = gen_server:wait_response(Promise5, infinity),
+
+ %% bad return value in the gen_server loop from handle_call.
+ {error,{{bad_return_value, badreturn},_}} = Async(my_test_name, badreturn),
+
+ %% Test other error cases
+ {error, {noproc,_}} = Async(Pid, started_p),
+ {error, {noproc,_}} = Async(my_test_name, started_p),
+ {error, {noconnection, _}} = Async({my_test_name, foo@node}, started_p),
+
+ {error, {noproc,_}} = Async({global, non_existing}, started_p),
+ catch exit(whereis(dummy_via), foo),
+ {'EXIT', {badarg,_}} =
+ (catch gen_server:send_request({via, dummy_via, non_existing}, started_p)),
+
+ %% Remote nodes
+ Via = dummy_via:reset(),
+ Remote = proplists:get_value(node,Config),
+ {ok, RPid} = rpc:call(Remote, gen_server, start, [{global, remote}, ?MODULE, [], []]),
+ dummy_via:register_name(remote, RPid),
+ {reply, ok} = Async(RPid, started_p),
+ {reply, ok} = Async({global, remote}, started_p),
+ {reply, ok} = Async({via, dummy_via, remote}, started_p),
+ {error, {shutdown, _}} = Async({global, remote}, stop_shutdown),
+ {error, {noproc, _}} = Async({global, remote}, started_p),
+ {error, {noproc, _}} = Async({via, dummy_via, remote}, started_p),
+ {error, {noproc, _}} = Async({via, dummy_via, non_existing}, started_p),
+
+ {ok, _} = rpc:call(Remote, gen_server, start, [{local, remote}, ?MODULE, [], []]),
+ {reply, ok} = Async({remote, Remote}, started_p),
+ {error, {shutdown, _}} = Async({remote, Remote}, stop_shutdown),
+ {error, {noproc, _}} = Async({remote, Remote}, started_p),
+
+ %% Cleanup
+ catch exit(Via, foo2),
+ receive {'EXIT', Via, foo2} -> ok end,
+ process_flag(trap_exit, OldFl),
+ ok.
+
+
+%% --------------------------------------
%% Test handle_continue.
%% --------------------------------------
@@ -1528,6 +1660,208 @@ wait_until_processed(Pid, Message, N) ->
ok
end.
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel,error_logger_format_depth),
+ application:unset_env(kernel,error_logger_format_depth),
+ Term = lists:seq(1,15),
+ Name = self(),
+ Report = #{label=>{gen_server,terminate},
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ log=>[],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ {F1,A1} = gen_server:format_log(Report),
+ FExpected1 = "** Generic server ~tp terminating \n"
+ "** Last message in was ~tp~n"
+ "** When Server state == ~tp~n"
+ "** Reason for termination ==~n** ~tp~n"
+ "** Client ~tp stacktrace~n"
+ "** ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp",[F1,A1]),
+ FExpected1=F1,
+ [Name,Term,Term,Term,clientname,[]] = A1,
+
+ Warning = #{label=>{gen_server,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ {WF1,WA1} = gen_server:format_log(Warning),
+ WFExpected1 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tp~n",
+ ct:log("WF1: ~ts~nWA1: ~tp",[WF1,WA1]),
+ WFExpected1=WF1,
+ [?MODULE,Term] = WA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel,error_logger_format_depth,Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = gen_server:format_log(#{label=>{gen_server,terminate},
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ log=>[],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}}),
+ FExpected2 = "** Generic server ~tP terminating \n"
+ "** Last message in was ~tP~n"
+ "** When Server state == ~tP~n"
+ "** Reason for termination ==~n** ~tP~n"
+ "** Client ~tP stacktrace~n"
+ "** ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp",[F2,A2]),
+ FExpected2=F2,
+ [Name,Depth,Limited,Depth,Limited,Depth,Limited,Depth,
+ clientname,Depth,[],Depth] = A2,
+
+ {WF2,WA2} = gen_server:format_log(Warning),
+ WFExpected2 = "** Undefined handle_info in ~p~n"
+ "** Unhandled message: ~tP~n",
+ ct:log("WF2: ~ts~nWA2: ~tp",[WF2,WA2]),
+ WFExpected2=WF2,
+ [?MODULE,Limited,Depth] = WA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel,error_logger_format_depth);
+ _ ->
+ application:set_env(kernel,error_logger_format_depth,FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1,15),
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Report = #{label=>{gen_server,terminate},
+ name=>Name,
+ last_message=>Term,
+ state=>Term,
+ log=>[],
+ reason=>Term,
+ client_info=>{self(),{clientname,[]}}},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report,FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** Generic server "++NameStr++" terminating \n"
+ "** Last message in was ",
+ ct:log("Str1: ~ts",[Str1]),
+ ct:log("length(Str1): ~p",[L1]),
+ true = lists:prefix(Expected1,Str1),
+
+ Warning = #{label=>{gen_server,no_handle_info},
+ module=>?MODULE,
+ message=>Term},
+ WStr1 = flatten_format_log(Warning,FormatOpts1),
+ WL1 = length(WStr1),
+ WExpected1 = "** Undefined handle_info in gen_server_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr1: ~ts",[WStr1]),
+ ct:log("length(WStr1): ~p",[WL1]),
+ true = lists:prefix(WExpected1,WStr1),
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report,FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** Generic server "++NameStr++" terminating \n"
+ "** Last message in was ",
+ ct:log("Str2: ~ts",[Str2]),
+ ct:log("length(Str2): ~p",[L2]),
+ true = lists:prefix(Expected2,Str2),
+ true = L2<L1,
+
+ WStr2 = flatten_format_log(Warning,FormatOpts2),
+ WL2 = length(WStr2),
+ WExpected2 = "** Undefined handle_info in gen_server_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr2: ~ts",[WStr2]),
+ ct:log("length(WStr2): ~p",[WL2]),
+ true = lists:prefix(WExpected2,WStr2),
+ true = WL2<WL1,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report,FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** Generic server "++NameStr++" terminating \n"
+ "** Last message in was ",
+ ct:log("Str3: ~ts",[Str3]),
+ ct:log("length(Str3): ~p",[L3]),
+ true = lists:prefix(Expected3,Str3),
+ true = L3<L1,
+
+ WFormatOpts3 = #{chars_limit=>80},
+ WStr3 = flatten_format_log(Warning,WFormatOpts3),
+ WL3 = length(WStr3),
+ WExpected3 = "** Undefined handle_info in gen_server_SUITE\n"
+ "** Unhandled message: ",
+ ct:log("WStr3: ~ts",[WStr3]),
+ ct:log("length(WStr3): ~p",[WL3]),
+ true = lists:prefix(WExpected3,WStr3),
+ true = WL3<WL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report,FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "Generic server "++NameStr++" terminating. Reason: ",
+ ct:log("Str4: ~ts",[Str4]),
+ ct:log("length(Str4): ~p",[L4]),
+ true = lists:prefix(Expected4,Str4),
+ true = L4<L1,
+
+ WStr4 = flatten_format_log(Warning,FormatOpts4),
+ WL4 = length(WStr4),
+ WExpected4 = "Undefined handle_info in gen_server_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr4: ~ts",[WStr4]),
+ ct:log("length(WStr4): ~p",[WL4]),
+ true = lists:prefix(WExpected4,WStr4),
+ true = WL4<WL1,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report,FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Generic server "++NameStr++" terminating. Reason: ",
+ ct:log("Str5: ~ts",[Str5]),
+ ct:log("length(Str5): ~p",[L5]),
+ true = lists:prefix(Expected5,Str5),
+ true = L5<L4,
+
+ WStr5 = flatten_format_log(Warning,FormatOpts5),
+ WL5 = length(WStr5),
+ WExpected5 = "Undefined handle_info in gen_server_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr5: ~ts",[WStr5]),
+ ct:log("length(WStr5): ~p",[WL5]),
+ true = lists:prefix(WExpected5,WStr5),
+ true = WL5<WL4,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>200},
+ Str6 = flatten_format_log(Report,FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Generic server "++NameStr++" terminating. Reason: ",
+ ct:log("Str6: ~ts",[Str6]),
+ ct:log("length(Str6): ~p",[L6]),
+ true = lists:prefix(Expected6,Str6),
+ true = L6<L4,
+
+ WFormatOpts6 = #{single_line=>true, chars_limit=>80},
+ WStr6 = flatten_format_log(Warning,WFormatOpts6),
+ WL6 = length(WStr6),
+ WExpected6 = "Undefined handle_info in gen_server_SUITE. "
+ "Unhandled message: ",
+ ct:log("WStr6: ~ts",[WStr6]),
+ ct:log("length(WStr6): ~p",[WL6]),
+ true = lists:prefix(WExpected6,WStr6),
+ true = WL6<WL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(gen_server:format_log(Report, Format)).
+
%%--------------------------------------------------------------
%% Help functions to spec_init_*
start_link(Init, Options) ->
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 296973370c..76dee868e9 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -42,7 +42,7 @@ all() ->
event_types, generic_timers, code_change,
{group, sys},
hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
- undef_in_terminate].
+ undef_in_terminate, {group, format_log}].
groups() ->
[{start, [], tcs(start)},
@@ -53,7 +53,8 @@ groups() ->
{abnormal_handle_event, [], tcs(abnormal)},
{sys, [], tcs(sys)},
{sys_handle_event, [], tcs(sys)},
- {undef_callbacks, [], tcs(undef_callbacks)}].
+ {undef_callbacks, [], tcs(undef_callbacks)},
+ {format_log, [], tcs(format_log)}].
tcs(start) ->
[start1, start2, start3, start4, start5, start6, start7,
@@ -69,7 +70,9 @@ tcs(sys) ->
get_state, replace_state];
tcs(undef_callbacks) ->
[undef_code_change, undef_terminate1, undef_terminate2,
- pop_too_many].
+ pop_too_many];
+tcs(format_log) ->
+ [format_log_1, format_log_2].
init_per_suite(Config) ->
Config.
@@ -140,8 +143,18 @@ start1(Config) ->
%% ?EXPECT_FAILURE(gen_statem:call(Pid0, hej), Reason),
%%process_flag(trap_exit, OldFl),
- ok = verify_empty_msgq().
+ ok = verify_empty_msgq(),
+ {ok,{Pid1,Mon1}} = gen_statem:start_monitor(?MODULE, start_arg(Config, []), []),
+ ok = do_func_test(Pid1),
+ ok = do_sync_func_test(Pid1),
+ stop_it(Pid1),
+ receive
+ {'DOWN', Mon1, process, Pid1, _Reason} ->
+ ok
+ end,
+ ok = verify_empty_msgq().
+
%% anonymous w. shutdown
start2(Config) ->
%% Dont link when shutdown
@@ -197,7 +210,7 @@ start6(Config) ->
ok = verify_empty_msgq().
-%% global register linked
+%% global register linked & monitored
start7(Config) ->
STM = {global,my_stm},
@@ -207,6 +220,8 @@ start7(Config) ->
gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
{error,{already_started,Pid}} =
gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
ok = do_func_test(Pid),
ok = do_sync_func_test(Pid),
@@ -214,6 +229,28 @@ start7(Config) ->
ok = do_sync_func_test(STM),
stop_it(STM),
+ ok = verify_empty_msgq(),
+
+ {ok,{Pid1,Mon1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid1),
+ ok = do_sync_func_test(Pid1),
+ ok = do_func_test(STM),
+ ok = do_sync_func_test(STM),
+ stop_it(STM),
+
+ receive
+ {'DOWN', Mon1, process, Pid1, _Reason} ->
+ ok
+ end,
+
ok = verify_empty_msgq().
@@ -237,7 +274,7 @@ start8(Config) ->
%%process_flag(trap_exit, OldFl),
ok = verify_empty_msgq().
-%% local register linked
+%% local register linked & monitored
start9(Config) ->
%%OldFl = process_flag(trap_exit, true),
Name = my_stm,
@@ -255,6 +292,24 @@ start9(Config) ->
stop_it(Pid),
%%process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq(),
+
+ {ok,{Pid1,Mon1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid1),
+ ok = do_sync_func_test(Pid1),
+ ok = do_func_test(Name),
+ ok = do_sync_func_test(Name),
+ stop_it(Pid1),
+
+ receive
+ {'DOWN', Mon1, process, Pid1, _Reason} ->
+ ok
+ end,
+
ok = verify_empty_msgq().
%% global register
@@ -398,19 +453,24 @@ stop7(Config) ->
stop8(Config) ->
Node = gen_statem_stop8,
{ok,NodeName} = ct_slave:start(Node),
- Dir = filename:dirname(code:which(?MODULE)),
- rpc:call(NodeName, code, add_path, [Dir]),
- {ok,Pid} =
- rpc:call(
- NodeName, gen_statem,start,
- [?MODULE,start_arg(Config, []),[]]),
- ok = gen_statem:stop(Pid),
- false = rpc:call(NodeName, erlang, is_process_alive, [Pid]),
- noproc =
- ?EXPECT_FAILURE(gen_statem:stop(Pid), Reason1),
- {ok,NodeName} = ct_slave:stop(Node),
+ Statem =
+ try
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:block_call(NodeName, code, add_path, [Dir]),
+ {ok,Pid} =
+ rpc:block_call(
+ NodeName, gen_statem,start,
+ [?MODULE,start_arg(Config, []),[]]),
+ ok = gen_statem:stop(Pid),
+ false = rpc:block_call(NodeName, erlang, is_process_alive, [Pid]),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(Pid), Reason1),
+ Pid
+ after
+ {ok,NodeName} = ct_slave:stop(Node)
+ end,
{{nodedown,NodeName},{sys,terminate,_}} =
- ?EXPECT_FAILURE(gen_statem:stop(Pid), Reason2),
+ ?EXPECT_FAILURE(gen_statem:stop(Statem), Reason2),
ok.
%% Registered name on remote node
@@ -419,21 +479,26 @@ stop9(Config) ->
LocalSTM = {local,Name},
Node = gen_statem__stop9,
{ok,NodeName} = ct_slave:start(Node),
- STM = {Name,NodeName},
- Dir = filename:dirname(code:which(?MODULE)),
- rpc:call(NodeName, code, add_path, [Dir]),
- {ok,Pid} =
- rpc:call(
- NodeName, gen_statem, start,
- [LocalSTM,?MODULE,start_arg(Config, []),[]]),
- ok = gen_statem:stop(STM),
- undefined = rpc:call(NodeName,erlang,whereis,[Name]),
- false = rpc:call(NodeName,erlang,is_process_alive,[Pid]),
- noproc =
- ?EXPECT_FAILURE(gen_statem:stop(STM), Reason1),
- {ok,NodeName} = ct_slave:stop(Node),
+ Statem =
+ try
+ STM = {Name,NodeName},
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:block_call(NodeName, code, add_path, [Dir]),
+ {ok,Pid} =
+ rpc:block_call(
+ NodeName, gen_statem, start,
+ [LocalSTM,?MODULE,start_arg(Config, []),[]]),
+ ok = gen_statem:stop(STM),
+ undefined = rpc:block_call(NodeName,erlang,whereis,[Name]),
+ false = rpc:block_call(NodeName,erlang,is_process_alive,[Pid]),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(STM), Reason1),
+ STM
+ after
+ {ok,NodeName} = ct_slave:stop(Node)
+ end,
{{nodedown,NodeName},{sys,terminate,_}} =
- ?EXPECT_FAILURE(gen_statem:stop(STM), Reason2),
+ ?EXPECT_FAILURE(gen_statem:stop(Statem), Reason2),
ok.
%% Globally registered name on remote node
@@ -441,18 +506,21 @@ stop10(Config) ->
Node = gen_statem_stop10,
STM = {global,to_stop},
{ok,NodeName} = ct_slave:start(Node),
- Dir = filename:dirname(code:which(?MODULE)),
- rpc:call(NodeName,code,add_path,[Dir]),
- {ok,Pid} =
- rpc:call(
- NodeName, gen_statem, start,
- [STM,?MODULE,start_arg(Config, []),[]]),
- global:sync(),
- ok = gen_statem:stop(STM),
- false = rpc:call(NodeName, erlang, is_process_alive, [Pid]),
- noproc =
- ?EXPECT_FAILURE(gen_statem:stop(STM), Reason1),
- {ok,NodeName} = ct_slave:stop(Node),
+ try
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:block_call(NodeName,code,add_path,[Dir]),
+ {ok,Pid} =
+ rpc:block_call(
+ NodeName, gen_statem, start,
+ [STM,?MODULE,start_arg(Config, []),[]]),
+ global:sync(),
+ ok = gen_statem:stop(STM),
+ false = rpc:block_call(NodeName, erlang, is_process_alive, [Pid]),
+ noproc =
+ ?EXPECT_FAILURE(gen_statem:stop(STM), Reason1)
+ after
+ {ok,NodeName} = ct_slave:stop(Node)
+ end,
noproc =
?EXPECT_FAILURE(gen_statem:stop(STM), Reason2),
ok.
@@ -466,10 +534,10 @@ abnormal1(Config) ->
gen_statem:start(LocalSTM, ?MODULE, start_arg(Config, []), []),
%% timeout call.
- delayed = gen_statem:call(Name, {delayed_answer,1}, 100),
+ delayed = gen_statem:call(Name, {delayed_answer,100}, 2000),
{timeout,_} =
?EXPECT_FAILURE(
- gen_statem:call(Name, {delayed_answer,1000}, 10),
+ gen_statem:call(Name, {delayed_answer,2000}, 100),
Reason),
ok = gen_statem:stop(Name),
?t:sleep(1100),
@@ -1369,7 +1437,7 @@ hibernate(Config) ->
{ok,Pid0} =
gen_statem:start_link(
?MODULE, start_arg(Config, hiber_now), []),
- is_in_erlang_hibernate(Pid0),
+ wait_erlang_hibernate(Pid0),
stop_it(Pid0),
receive
{'EXIT',Pid0,normal} -> ok
@@ -1382,23 +1450,23 @@ hibernate(Config) ->
true = ({current_function,{erlang,hibernate,3}} =/=
erlang:process_info(Pid,current_function)),
hibernating = gen_statem:call(Pid, hibernate_sync),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
good_morning = gen_statem:call(Pid, wakeup_sync),
is_not_in_erlang_hibernate(Pid),
hibernating = gen_statem:call(Pid, hibernate_sync),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
please_just_five_more = gen_statem:call(Pid, snooze_sync),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
good_morning = gen_statem:call(Pid, wakeup_sync),
is_not_in_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, hibernate_async),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, wakeup_async),
is_not_in_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, hibernate_async),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, snooze_async),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, wakeup_async),
is_not_in_erlang_hibernate(Pid),
@@ -1406,14 +1474,14 @@ hibernate(Config) ->
true =
({current_function,{erlang,hibernate,3}} =/=
erlang:process_info(Pid, current_function)),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
'alive!' = gen_statem:call(Pid, 'alive?'),
true =
({current_function,{erlang,hibernate,3}} =/=
erlang:process_info(Pid, current_function)),
Pid ! hibernate_now,
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
'alive!' = gen_statem:call(Pid, 'alive?'),
true =
@@ -1421,34 +1489,34 @@ hibernate(Config) ->
erlang:process_info(Pid, current_function)),
hibernating = gen_statem:call(Pid, hibernate_sync),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
good_morning = gen_statem:call(Pid, wakeup_sync),
is_not_in_erlang_hibernate(Pid),
hibernating = gen_statem:call(Pid, hibernate_sync),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
please_just_five_more = gen_statem:call(Pid, snooze_sync),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
good_morning = gen_statem:call(Pid, wakeup_sync),
is_not_in_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, hibernate_async),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, wakeup_async),
is_not_in_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, hibernate_async),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, snooze_async),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
ok = gen_statem:cast(Pid, wakeup_async),
is_not_in_erlang_hibernate(Pid),
hibernating = gen_statem:call(Pid, hibernate_sync),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
sys:suspend(Pid),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
sys:resume(Pid),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
receive after 1000 -> ok end,
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
good_morning = gen_statem:call(Pid, wakeup_sync),
is_not_in_erlang_hibernate(Pid),
@@ -1464,15 +1532,16 @@ hibernate(Config) ->
%% Auto-hibernation timeout
auto_hibernate(Config) ->
OldFl = process_flag(trap_exit, true),
- HibernateAfterTimeout = 100,
+ HibernateAfterTimeout = 1000,
{ok,Pid} =
gen_statem:start_link(
- ?MODULE, start_arg(Config, []), [{hibernate_after, HibernateAfterTimeout}]),
+ ?MODULE, start_arg(Config, []),
+ [{hibernate_after, HibernateAfterTimeout}]),
%% After init test
is_not_in_erlang_hibernate(Pid),
timer:sleep(HibernateAfterTimeout),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
%% After info test
Pid ! {hping, self()},
receive
@@ -1483,7 +1552,7 @@ auto_hibernate(Config) ->
end,
is_not_in_erlang_hibernate(Pid),
timer:sleep(HibernateAfterTimeout),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
%% After cast test
ok = gen_statem:cast(Pid, {hping, self()}),
receive
@@ -1494,42 +1563,42 @@ auto_hibernate(Config) ->
end,
is_not_in_erlang_hibernate(Pid),
timer:sleep(HibernateAfterTimeout),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
%% After call test
hpong = gen_statem:call(Pid, hping),
is_not_in_erlang_hibernate(Pid),
timer:sleep(HibernateAfterTimeout),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
%% Timer test 1
- TimerTimeout1 = 50,
- ok = gen_statem:call(Pid, {arm_htimer, self(), TimerTimeout1}),
+ TimerTimeout1 = HibernateAfterTimeout div 2,
+ ok = gen_statem:call(Pid, {start_htimer, self(), TimerTimeout1}),
is_not_in_erlang_hibernate(Pid),
timer:sleep(TimerTimeout1),
is_not_in_erlang_hibernate(Pid),
receive
- {Pid, htimer_armed} ->
+ {Pid, htimer_timeout} ->
ok
after 1000 ->
ct:fail(timer1)
end,
is_not_in_erlang_hibernate(Pid),
timer:sleep(HibernateAfterTimeout),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
%% Timer test 2
- TimerTimeout2 = 150,
- ok = gen_statem:call(Pid, {arm_htimer, self(), TimerTimeout2}),
+ TimerTimeout2 = HibernateAfterTimeout * 2,
+ ok = gen_statem:call(Pid, {start_htimer, self(), TimerTimeout2}),
is_not_in_erlang_hibernate(Pid),
timer:sleep(HibernateAfterTimeout),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
receive
- {Pid, htimer_armed} ->
+ {Pid, htimer_timeout} ->
ok
- after 1000 ->
+ after TimerTimeout2 ->
ct:fail(timer2)
end,
is_not_in_erlang_hibernate(Pid),
timer:sleep(HibernateAfterTimeout),
- is_in_erlang_hibernate(Pid),
+ wait_erlang_hibernate(Pid),
stop_it(Pid),
process_flag(trap_exit, OldFl),
receive
@@ -1539,38 +1608,38 @@ auto_hibernate(Config) ->
end,
ok = verify_empty_msgq().
-is_in_erlang_hibernate(Pid) ->
+
+wait_erlang_hibernate(Pid) ->
receive after 1 -> ok end,
- is_in_erlang_hibernate_1(200, Pid).
+ wait_erlang_hibernate_1(200, Pid).
-is_in_erlang_hibernate_1(0, Pid) ->
+wait_erlang_hibernate_1(0, Pid) ->
ct:log("~p\n", [erlang:process_info(Pid, current_function)]),
- ct:fail(not_in_erlang_hibernate_3);
-is_in_erlang_hibernate_1(N, Pid) ->
+ ct:fail(should_be_in_erlang_hibernate_3);
+wait_erlang_hibernate_1(N, Pid) ->
{current_function,MFA} = erlang:process_info(Pid, current_function),
case MFA of
{erlang,hibernate,3} ->
ok;
_ ->
receive after 10 -> ok end,
- is_in_erlang_hibernate_1(N-1, Pid)
+ wait_erlang_hibernate_1(N-1, Pid)
end.
is_not_in_erlang_hibernate(Pid) ->
receive after 1 -> ok end,
is_not_in_erlang_hibernate_1(200, Pid).
-is_not_in_erlang_hibernate_1(0, Pid) ->
- ct:log("~p\n", [erlang:process_info(Pid, current_function)]),
- ct:fail(not_in_erlang_hibernate_3);
+is_not_in_erlang_hibernate_1(0, _Pid) ->
+ ct:fail(should_not_be_in_erlang_hibernate_3);
is_not_in_erlang_hibernate_1(N, Pid) ->
{current_function,MFA} = erlang:process_info(Pid, current_function),
case MFA of
- {erlang,hibernate,3} ->
+ {erlang,hibernate,3} ->
receive after 10 -> ok end,
is_not_in_erlang_hibernate_1(N-1, Pid);
- _ ->
- ok
+ _ ->
+ ok
end.
@@ -1836,6 +1905,315 @@ next_events(Config) ->
?EXPECT_FAILURE(gen_statem:stop(Pid), Reason).
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1,15),
+ Name = self(),
+ Reason = {bad_reply_action_from_state_function,[]},
+ Report1 = simple_report(Name, Term, Reason),
+ Report2 = elaborate_report(Name, Term, Reason),
+
+ {F1,A1} = gen_statem:format_log(Report1),
+ ct:log("F1: ~ts~nA1: ~tp",[F1,A1]),
+ FExpected1 = "** State machine ~tp terminating~n"
+ "** When server state = ~tp~n"
+ "** Reason for termination = ~tp:~tp~n"
+ "** Callback modules = ~tp~n"
+ "** Callback mode = ~tp~n",
+ FExpected1 = F1,
+ [Name,Term,error,Reason,[?MODULE],state_functions] = A1,
+
+ {F3,A3} = gen_statem:format_log(Report2),
+ ct:log("F3: ~ts~nA3: ~tp",[F3,A3]),
+ FExpected3 = "** State machine ~tp terminating~n"
+ "** Last event = ~tp~n"
+ "** When server state = ~tp~n"
+ "** Reason for termination = ~tp:~tp~n"
+ "** Callback modules = ~tp~n"
+ "** Callback mode = ~tp~n"
+ "** Queued = ~tp~n"
+ "** Postponed = ~tp~n"
+ "** Stacktrace =~n** ~tp~n"
+ "** Time-outs: ~tp~n"
+ "** Log =~n** ~tp~n"
+ "** Client ~tp stacktrace~n"
+ "** ~tp~n",
+ FExpected3 = F3,
+ Stacktrace = stacktrace(),
+ [Name,Term,Term,error,Reason,[?MODULE],[state_functions,state_enter],[Term],
+ [{internal,Term}],Stacktrace,{1,[{timeout,message}]},[Term],Name,[]] = A3,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = gen_statem:format_log(Report1),
+ ct:log("F2: ~ts~nA2: ~tp",[F2,A2]),
+ FExpected2 = "** State machine ~tP terminating~n"
+ "** When server state = ~tP~n"
+ "** Reason for termination = ~tP:~tP~n"
+ "** Callback modules = ~tP~n"
+ "** Callback mode = ~tP~n",
+ FExpected2 = F2,
+ [Name,Depth,Limited,Depth,error,Depth,Reason,Depth,
+ [?MODULE],Depth,state_functions,Depth] = A2,
+
+ {F4,A4} = gen_statem:format_log(Report2),
+ ct:log("F4: ~ts~nA4: ~tp",[F4,A4]),
+ FExpected4 = "** State machine ~tP terminating~n"
+ "** Last event = ~tP~n"
+ "** When server state = ~tP~n"
+ "** Reason for termination = ~tP:~tP~n"
+ "** Callback modules = ~tP~n"
+ "** Callback mode = ~tP~n"
+ "** Queued = ~tP~n"
+ "** Postponed = ~tP~n"
+ "** Stacktrace =~n** ~tP~n"
+ "** Time-outs: ~tP~n"
+ "** Log =~n** ~tP~n"
+ "** Client ~tP stacktrace~n"
+ "** ~tP~n",
+ FExpected4 = F4,
+ LimitedPostponed = [{internal,[1,2,3,4,5,6,'...']}],
+ LimitedStacktrace = io_lib:limit_term(Stacktrace, Depth),
+ LimitedQueue = io_lib:limit_term([Term], Depth),
+ [Name,Depth,Limited,Depth,Limited,Depth,error,Depth,Reason,Depth,
+ [?MODULE],Depth,[state_functions,state_enter],Depth,LimitedQueue,Depth,
+ LimitedPostponed,Depth,LimitedStacktrace,Depth,{1,[{timeout,message}]},
+ Depth,[Limited],Depth,Name,Depth,[],Depth] = A4,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ format_log_2_simple(),
+ format_log_2_elaborate(),
+ ok.
+
+format_log_2_simple() ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+
+ Term = lists:seq(1,15),
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Reason = {bad_reply_action_from_state_function,[]},
+ Report = simple_report(Name, Term, Reason),
+
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** When server state = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ "** Reason for termination = "
+ "error:{bad_reply_action_from_state_function,[]}\n"
+ "** Callback modules = ["?MODULE_STRING"]\n"
+ "** Callback mode = state_functions\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ Expected1 = Str1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** When server state = [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Reason for termination = "
+ "error:{bad_reply_action_from_state_function,[]}\n"
+ "** Callback modules = ["?MODULE_STRING"]\n"
+ "** Callback mode = state_functions\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** When server state = [",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: {bad_reply_action_from_state_function,[]}. "
+ "State: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ Expected4 = Str4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: {bad_reply_action_from_state_function,[]}. "
+ "State: [1,2,3,4,5,6,7,8,9|...].",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ Expected5 = Str5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>100},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: ",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+format_log_2_elaborate() ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+
+ Term = lists:seq(1,15),
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Reason = {bad_reply_action_from_state_function,[]},
+ Report = elaborate_report(Name, Term, Reason),
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** Last event = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = lists:prefix(Expected1, Str1),
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** Last event = [1,2,3,4,5,6,7,8,9|...]\n"
+ "** When server state = [1,2,3,4,5,6,7,8,9|...]\n"
+ "** Reason for termination = "
+ "error:{bad_reply_action_from_state_function,[]}\n"
+ "** Callback modules = ["?MODULE_STRING"]\n"
+ "** Callback mode = [state_functions,state_enter]\n"
+ "** Queued = [[1,2,3,4,5,6,7,8|...]]\n"
+ "** Postponed = [{internal,[1,2,3,4,5,6|...]}]\n"
+ "** Stacktrace =\n"
+ "** [{m,f,1,[1,2,3,4|...]}]\n"
+ "** Time-outs: {1,[{timeout,message}]}\n"
+ "** Log =\n"
+ "** [[1,2,3,4,5,6,7,8|...]]\n"
+ "** Client "++NameStr ++ " stacktrace\n"
+ "** []\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ Expected2 = Str2,
+
+ FormatOpts3 = #{chars_limit=>300},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = "** State machine " ++ NameStr ++ " terminating\n"
+ "** Last event = ",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: {bad_reply_action_from_state_function,[]}. "
+ "Stack: [{m,f,1,[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]}]. "
+ "Last event: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "State: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Log: [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]. "
+ "Client " ++ NameStr ++ " stacktrace: [].",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ Expected4 = Str4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason: {bad_reply_action_from_state_function,[]}. "
+ "Stack: [{m,f,1,[1,2,3,4|...]}]. "
+ "Last event: [1,2,3,4,5,6,7,8,9|...]. "
+ "State: [1,2,3,4,5,6,7,8,9|...]. "
+ "Log: [[1,2,3,4,5,6,7,8|...]]. "
+ "Client " ++ NameStr ++ " stacktrace: [].",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ Expected5 = Str5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>300},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "State machine " ++ NameStr ++ " terminating. "
+ "Reason:",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+simple_report(Name, Term, Reason) ->
+ #{label=>{gen_statem,terminate},
+ name=>Name,
+ queue=>[],
+ postponed=>[],
+ modules=>[?MODULE],
+ callback_mode=>state_functions,
+ state_enter=>false,
+ state=>Term,
+ timeouts=>{0,[]},
+ log=>[],
+ reason=>{error,Reason,[]},
+ client_info=>undefined}.
+
+elaborate_report(Name, Term, Reason) ->
+ #{label=>{gen_statem,terminate},
+ name=>Name,
+ queue=>[Term,Term],
+ postponed=>[{internal,Term}],
+ modules=>[?MODULE],
+ callback_mode=>state_functions,
+ state_enter=>true,
+ state=>Term,
+ timeouts=>{1,[{timeout,message}]},
+ log=>[Term],
+ reason=>{error,Reason,stacktrace()},
+ client_info=>{self(),{self(),[]}}}.
+
+stacktrace() ->
+ [{m,f,1,lists:seq(1, 15)}].
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(gen_statem:format_log(Report, Format)).
+
%%
%% Functionality check
%%
@@ -1871,7 +2249,17 @@ do_func_test(STM) ->
wfor(yes),
ok = do_disconnect(STM),
ok = gen_statem:cast(STM, {'alive?',self()}),
+ P0 = gen_statem:send_request(STM, 'alive?'),
+ timeout = gen_statem:wait_response(P0, 0),
wfor(yes),
+ {reply, yes} = gen_statem:wait_response(P0, infinity),
+ _ = flush(),
+ P1 = gen_statem:send_request(STM, 'alive?'),
+ receive Msg ->
+ no_reply = gen_statem:check_response(Msg, P0),
+ {reply, yes} = gen_statem:check_response(Msg, P1)
+ after 1000 -> exit(timeout)
+ end,
ok.
@@ -1966,13 +2354,13 @@ init(stop_shutdown) ->
{stop,shutdown};
init(sleep) ->
?t:sleep(1000),
- {ok,idle,data};
+ init_sup({ok,idle,data});
init(hiber) ->
- {ok,hiber_idle,[]};
+ init_sup({ok,hiber_idle,[]});
init(hiber_now) ->
- {ok,hiber_idle,[],[hibernate]};
+ init_sup({ok,hiber_idle,[],[hibernate]});
init({data, Data}) ->
- {ok,idle,Data};
+ init_sup({ok,idle,Data});
init({callback_mode,CallbackMode,Arg}) ->
ets:new(?MODULE, [named_table,private]),
ets:insert(?MODULE, {callback_mode,CallbackMode}),
@@ -1982,14 +2370,35 @@ init({map_statem,#{init := Init}=Machine,Modes}) ->
ets:insert(?MODULE, {callback_mode,[handle_event_function|Modes]}),
case Init() of
{ok,State,Data,Ops} ->
- {ok,State,[Data|Machine],Ops};
+ init_sup({ok,State,[Data|Machine],Ops});
{ok,State,Data} ->
- {ok,State,[Data|Machine]};
+ init_sup({ok,State,[Data|Machine]});
Other ->
- Other
+ init_sup(Other)
end;
init([]) ->
- {ok,idle,data}.
+ init_sup({ok,idle,data}).
+
+%% Supervise state machine parent i.e the test case, and if it dies
+%% (fails due to some reason), kill the state machine,
+%% just to not leak resources (process, name, ETS table, etc...)
+%%
+init_sup(Result) ->
+ Parent = gen:get_parent(),
+ Statem = self(),
+ _Supervisor =
+ spawn(
+ fun () ->
+ StatemRef = monitor(process, Statem),
+ ParentRef = monitor(process, Parent),
+ receive
+ {'DOWN', StatemRef, _, _, Reason} ->
+ exit(Reason);
+ {'DOWN', ParentRef, _, _, _} ->
+ exit(Statem, kill)
+ end
+ end),
+ Result.
callback_mode() ->
try ets:lookup(?MODULE, callback_mode) of
@@ -2022,10 +2431,10 @@ idle(cast, {hping,Pid}, Data) ->
{keep_state, Data};
idle({call, From}, hping, _Data) ->
{keep_state_and_data, [{reply, From, hpong}]};
-idle({call, From}, {arm_htimer, Pid, Timeout}, _Data) ->
- {keep_state_and_data, [{reply, From, ok}, {timeout, Timeout, {arm_htimer, Pid}}]};
-idle(timeout, {arm_htimer, Pid}, _Data) ->
- Pid ! {self(), htimer_armed},
+idle({call, From}, {start_htimer, Pid, Timeout}, _Data) ->
+ {keep_state_and_data, [{reply, From, ok}, {timeout, Timeout, {htimer, Pid}}]};
+idle(timeout, {htimer, Pid}, _Data) ->
+ Pid ! {self(), htimer_timeout},
keep_state_and_data;
idle(cast, {connect,Pid}, Data) ->
Pid ! accept,
diff --git a/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl b/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl
index 1bcd08867f..1de7de527b 100644
--- a/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl
+++ b/lib/stdlib/test/gen_statem_SUITE_data/oc_statem.erl
@@ -31,6 +31,24 @@ start(Opts) ->
gen_statem:start({local, ?MODULE}, ?MODULE, [], Opts).
init([]) ->
+ %% Supervise state machine parent i.e the test case, and if it dies
+ %% (fails due to some reason), kill the state machine,
+ %% just to not leak resources (process, name, ETS table, etc...)
+ %%
+ Parent = gen:get_parent(),
+ Statem = self(),
+ _Supervisor =
+ spawn(
+ fun () ->
+ StatemRef = monitor(process, Statem),
+ ParentRef = monitor(process, Parent),
+ receive
+ {'DOWN', StatemRef, _, _, Reason} ->
+ exit(Reason);
+ {'DOWN', ParentRef, _, _, _} ->
+ exit(Statem, kill)
+ end
+ end),
{ok, start, #{}}.
callback_mode() ->
diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl
index e497b2fb5d..df6958cfa9 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-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1568,10 +1568,6 @@ request({put_chars, Encoding, Chars}, State) ->
request({put_chars, Encoding, Module, Function, Args}, State) ->
{ok, ok, State#state{q=[{put_chars, Encoding, Module, Function, Args} |
State#state.q ]}};
-request({put_chars,Chars}, State) ->
- {ok, ok, State#state{q=[{put_chars, Chars} | State#state.q ]}};
-request({put_chars,M,F,As}, State) ->
- {ok, ok, State#state{q=[{put_chars, M,F,As} | State#state.q ]}};
request({get_until, Encoding, Prompt, M, F, As}, State) ->
{ok, convert(State#state.nxt, Encoding, State#state.mode), State#state{nxt = eof, q = [{get_until, Encoding, Prompt, M, F, As} | State#state.q]}};
request({get_chars, Encoding, Prompt, N}, State) ->
@@ -1583,20 +1579,6 @@ request({get_line, Encoding, Prompt}, State) ->
State#state{nxt = eof,
q = [{get_line, Encoding, Prompt} |
State#state.q]}};
-request({get_until, Prompt, M, F, As}, State) ->
- {ok, convert(State#state.nxt, latin1, State#state.mode),
- State#state{nxt = eof,
- q = [{get_until, Prompt, M, F, As} | State#state.q]}};
-request({get_chars, Prompt, N}, State) ->
- {ok, convert(State#state.nxt, latin1, State#state.mode),
- State#state{nxt = eof,
- q = [{get_chars, Prompt, N} |
- State#state.q]}};
-request({get_line, Prompt}, State) ->
- {ok, convert(State#state.nxt, latin1, State#state.mode),
- State#state{nxt = eof,
- q = [{get_line, Prompt} |
- State#state.q]}};
request({get_geomentry,_}, State) ->
{error, {error,enotsup}, State};
request({setopts, Opts}, State) when Opts =:= [{binary, false}]; Opts =:= [list] ->
diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl
index 19dd755a7c..7a0ffee9e1 100644
--- a/lib/stdlib/test/lists_SUITE.erl
+++ b/lib/stdlib/test/lists_SUITE.erl
@@ -2229,6 +2229,7 @@ sublist_2_e(Config) when is_list(Config) ->
sublist_3(Config) when is_list(Config) ->
[] = lists:sublist([], 1, 0),
[] = lists:sublist([], 1, 1),
+ [] = lists:sublist([], 2, 0),
[] = lists:sublist([a], 1, 0),
[a] = lists:sublist([a], 1, 1),
[a] = lists:sublist([a], 1, 2),
@@ -2242,6 +2243,7 @@ sublist_3(Config) when is_list(Config) ->
[] = lists:sublist([a], 2, 1),
[] = lists:sublist([a], 2, 2),
[] = lists:sublist([a], 2, 79),
+ [] = lists:sublist([a], 3, 1),
[] = lists:sublist([a,b|c], 1, 0),
[] = lists:sublist([a,b|c], 2, 0),
[a] = lists:sublist([a,b|c], 1, 1),
diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl
index 6f3cd8bf1b..0ad6989cbb 100644
--- a/lib/stdlib/test/maps_SUITE.erl
+++ b/lib/stdlib/test/maps_SUITE.erl
@@ -30,7 +30,7 @@
-export([t_update_with_3/1, t_update_with_4/1,
t_get_3/1, t_filter_2/1,
t_fold_3/1,t_map_2/1,t_size_1/1,
- t_iterator_1/1,
+ t_iterator_1/1, t_put_opt/1, t_merge_opt/1,
t_with_2/1,t_without_2/1]).
%%-define(badmap(V,F,Args), {'EXIT', {{badmap,V}, [{maps,F,Args,_}|_]}}).
@@ -48,7 +48,7 @@ all() ->
[t_update_with_3,t_update_with_4,
t_get_3,t_filter_2,
t_fold_3,t_map_2,t_size_1,
- t_iterator_1,
+ t_iterator_1,t_put_opt,t_merge_opt,
t_with_2,t_without_2].
t_update_with_3(Config) when is_list(Config) ->
@@ -204,6 +204,28 @@ iter_kv(I) ->
[{K,V} | iter_kv(NI)]
end.
+t_put_opt(Config) when is_list(Config) ->
+ Value = id(#{complex => map}),
+ Map = id(#{a => Value}),
+ true = erts_debug:same(maps:put(a, Value, Map), Map),
+ ok.
+
+t_merge_opt(Config) when is_list(Config) ->
+ Small = id(#{a => 1}),
+ true = erts_debug:same(maps:merge(#{}, Small), Small),
+ true = erts_debug:same(maps:merge(Small, #{}), Small),
+ true = erts_debug:same(maps:merge(Small, Small), Small),
+
+ Large = maps:from_list([{I,I}||I<-lists:seq(1,200)]),
+ true = erts_debug:same(maps:merge(#{}, Large), Large),
+ true = erts_debug:same(maps:merge(Large, #{}), Large),
+ true = erts_debug:same(maps:merge(Large, Large), Large),
+
+ List = id([a|b]),
+ ?badmap([a|b],merge,[[a|b],[a|b]]) = (catch maps:merge(List, List)),
+
+ ok.
+
t_size_1(Config) when is_list(Config) ->
0 = maps:size(#{}),
10 = maps:size(maps:from_list([{{"k",I},I}||I<-lists:seq(1,10)])),
@@ -215,8 +237,11 @@ t_size_1(Config) when is_list(Config) ->
600 = maps:size(maps:from_list([{{"k",I},I}||I<-lists:seq(1,600)])),
%% error case
- ?badmap(a,size,[a]) = (catch maps:size(id(a))),
- ?badmap(<<>>,size,[<<>>]) = (catch maps:size(id(<<>>))),
+ %%
+ %% Note that the stack trace is ignored because the compiler may have
+ %% rewritten maps:size/2 to map_size.
+ {'EXIT', {{badmap,a}, _}} = (catch maps:size(id(a))),
+ {'EXIT', {{badmap,<<>>}, _}} = (catch maps:size(id(<<>>))),
ok.
id(I) -> I.
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index 127b1317e4..b3673efb5a 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-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,8 +27,12 @@
-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, stacktrace/1, sync_start_nolink/1, sync_start_link/1,
- spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, '\x{447}'/0,
- hibernate/1, stop/1, t_format/1, t_format_arbitrary/1]).
+ sync_start_monitor/1, sync_start_monitor_link/1,
+ sync_start_timeout/1, sync_start_link_timeout/1,
+ sync_start_monitor_link_timeout/1,
+ spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, sp6/1, sp7/1,
+ sp8/1, sp9/1, sp10/1,
+ '\x{447}'/0, hibernate/1, stop/1, t_format/1, t_format_arbitrary/1]).
-export([ otp_6345/1, init_dont_hang/1]).
-export([hib_loop/1, awaken/1]).
@@ -39,6 +43,8 @@
-export([otp_6345_init/1, init_dont_hang_init/1]).
+-export([report_cb/1, report_cb_chars_limit/1, log/2, rcb_tester/0]).
+
-export([system_terminate/4]).
-ifdef(STANDALONE).
@@ -51,11 +57,14 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[crash, stacktrace, {group, sync_start}, spawn_opt, hibernate,
- {group, tickets}, stop, t_format, t_format_arbitrary].
+ {group, tickets}, stop, t_format, t_format_arbitrary, report_cb].
groups() ->
[{tickets, [], [otp_6345, init_dont_hang]},
- {sync_start, [], [sync_start_nolink, sync_start_link]}].
+ {sync_start, [], [sync_start_nolink, sync_start_link,
+ sync_start_monitor, sync_start_monitor_link,
+ sync_start_timeout, sync_start_link_timeout,
+ sync_start_monitor_link_timeout]}].
init_per_suite(Config) ->
Config.
@@ -275,6 +284,84 @@ sync_start_link(Config) when is_list(Config) ->
end,
ok.
+sync_start_monitor(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp6, [self()]),
+ receive
+ {sync_started, _} -> ct:fail(async_start)
+ after 1000 -> ok
+ end,
+ receive
+ {Pid2, init} ->
+ Pid2 ! go_on
+ end,
+ receive
+ {sync_started, _} -> ok
+ after 1000 -> ct:fail(no_sync_start)
+ end,
+ receive received_down -> ok
+ after 2000 -> ct:fail(no_down)
+ end,
+ ok.
+
+sync_start_monitor_link(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp7, [self()]),
+ receive
+ {sync_started, _} -> ct:fail(async_start)
+ after 1000 -> ok
+ end,
+ receive
+ {Pid2, init} ->
+ Pid2 ! go_on
+ end,
+ receive
+ {sync_started, _} -> ok
+ after 1000 -> ct:fail(no_sync_start)
+ end,
+ receive received_down -> ok
+ after 1000 -> ct:fail(no_down)
+ end,
+ receive received_exit -> ok
+ after 1000 -> ct:fail(no_exit)
+ end,
+ ok.
+
+sync_start_timeout(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp8, [self()]),
+ receive done -> ok end,
+ receive {received_exit, _} = M1 -> ct:fail(M1)
+ after 0 -> ok
+ end,
+ receive {received_down, _} = M2 -> ct:fail(M2)
+ after 0 -> ok
+ end,
+ ok.
+
+sync_start_link_timeout(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp9, [self()]),
+ receive done -> ok end,
+ receive {received_exit, _} = M1 -> ct:fail(M1)
+ after 0 -> ok
+ end,
+ receive {received_down, _} = M2 -> ct:fail(M2)
+ after 0 -> ok
+ end,
+ ok.
+
+sync_start_monitor_link_timeout(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp10, [self()]),
+ receive done -> ok end,
+ receive {received_exit, _} = M1 -> ct:fail(M1)
+ after 0 -> ok
+ end,
+ receive
+ {received_down, R} ->
+ killed = R,
+ ok
+ after 0 -> ct:fail(no_down)
+ end,
+ ok.
+
+
spawn_opt(Config) when is_list(Config) ->
F = fun sp1/0,
{name,Fname} = erlang:fun_info(F, name),
@@ -313,6 +400,89 @@ sp5(Tester) ->
Pid = proc_lib:start(?MODULE, sp4, [self(), Tester]),
Tester ! {sync_started, Pid}.
+sp6(Tester) ->
+ process_flag(trap_exit, true),
+ {Pid, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester]),
+ Tester ! {sync_started, Pid},
+ receive
+ {'EXIT', Pid, normal} ->
+ exit(received_exit)
+ after 1000 ->
+ ok
+ end,
+ receive
+ {'DOWN', Mon, process, Pid, normal} ->
+ Tester ! received_down
+ end.
+
+sp7(Tester) ->
+ process_flag(trap_exit, true),
+ {Pid, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester], infinity, [link]),
+ Tester ! {sync_started, Pid},
+ receive
+ {'EXIT', Pid, normal} ->
+ Tester ! received_exit
+ end,
+ receive
+ {'DOWN', Mon, process, Pid, normal} ->
+ Tester ! received_down
+ end.
+
+sp8(Tester) ->
+ process_flag(trap_exit, true),
+ {error,timeout} = proc_lib:start(?MODULE, sp4, [self(), Tester], 500, [link]),
+ receive after 500 -> ok end,
+ receive
+ {'EXIT', _Pid1, Reason1} ->
+ Tester ! {received_exit, Reason1}
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', _Mon2, process, _Pid2, Reason2} ->
+ Tester ! {received_down, Reason2}
+ after 0 ->
+ ok
+ end,
+ Tester ! done.
+
+sp9(Tester) ->
+ process_flag(trap_exit, true),
+ {error,timeout} = proc_lib:start_link(?MODULE, sp4, [self(), Tester], 500),
+ receive after 500 -> ok end,
+ receive
+ {'EXIT', _Pid1, Reason1} ->
+ Tester ! {received_exit, Reason1}
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', _Mon, process, _Pid2, Reason2} ->
+ Tester ! {received_down, Reason2}
+ after 0 ->
+ ok
+ end,
+ Tester ! done.
+
+
+sp10(Tester) ->
+ process_flag(trap_exit, true),
+ {{error,timeout}, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester], 500, [link]),
+ receive after 500 -> ok end,
+ receive
+ {'EXIT', _Pid1, Reason1} ->
+ Tester ! {received_exit, Reason1}
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', Mon, process, _Pid2, Reason2} ->
+ Tester ! {received_down, Reason2}
+ after 0 ->
+ ok
+ end,
+ Tester ! done.
+
sp4(Parent, Tester) ->
Tester ! {self(), init},
receive
@@ -421,10 +591,12 @@ hib_receive_messages(N) ->
%% 'monitor' spawn_opt option.
otp_6345(Config) when is_list(Config) ->
Opts = [link,monitor],
- {'EXIT', {badarg,[{proc_lib,check_for_monitor,_,_}|_Stack]}} =
- (catch proc_lib:start(?MODULE, otp_6345_init, [self()],
- 1000, Opts)),
- ok.
+ try
+ blupp = proc_lib:start(?MODULE, otp_6345_init, [self()],
+ 1000, Opts)
+ catch
+ error:badarg -> ok
+ end.
otp_6345_init(Parent) ->
proc_lib:init_ack(Parent, {ok, self()}),
@@ -628,6 +800,180 @@ do_test_format(Report, Encoding, Depth) ->
'\x{aaa}t_format_looper'()
end.
+%% Test report callback for any Logger handler
+report_cb(_Config) ->
+ ok = logger:add_handler(?MODULE,?MODULE,#{config=>self()}),
+ Pid = proc_lib:spawn(?MODULE, sp2, []),
+ ct:sleep(100),
+ {links,[NPid]} = process_info(Pid,links),
+ NPidStr = pid_to_list(NPid),
+ Pid ! die,
+ Report =
+ receive
+ {report,R} ->
+ R
+ after 5000 ->
+ ct:fail(no_report_received)
+ end,
+
+ Str1 = flatten_report_cb(Report,#{}),
+ L1 = length(Str1),
+ Expected1 = " crasher:\n initial call: proc_lib_SUITE:sp2/0\n",
+ ct:log("Str1: ~p",[Str1]),
+ ct:log("length(Str1): ~p",[L1]),
+ true = lists:prefix(Expected1,Str1),
+
+ FormatOpts1 = #{},
+ Str1 = flatten_report_cb(Report,FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = " crasher:\n initial call: proc_lib_SUITE:sp2/0\n",
+ ct:log("Str1: ~p",[Str1]),
+ ct:log("length(Str1): ~p",[L1]),
+ true = lists:prefix(Expected1,Str1),
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_report_cb(Report,FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = " crasher:\n initial call: proc_lib_SUITE:sp2/0\n",
+ ct:log("Str2: ~p",[Str2]),
+ ct:log("length(Str2): ~p",[L2]),
+ true = lists:prefix(Expected2,Str2),
+ true = L2<L1,
+
+ FormatOpts3 = #{chars_limit=>500},
+ Str3 = flatten_report_cb(Report,FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = " crasher:\n initial call: proc_lib_SUITE:sp2/0\n",
+ ct:log("Str3: ~p",[Str3]),
+ ct:log("length(Str3): ~p",[L3]),
+ true = lists:prefix(Expected3,Str3),
+ true = L3<L1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_report_cb(Report,FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "crasher: initial call: proc_lib_SUITE:sp2/0,",
+ ct:log("Str4: ~p",[Str4]),
+ ct:log("length(Str4): ~p",[L4]),
+ true = lists:prefix(Expected4,Str4),
+ true = L4<L1,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_report_cb(Report,FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "crasher: initial call: proc_lib_SUITE:sp2/0,",
+ ct:log("Str5: ~p",[Str5]),
+ ct:log("length(Str5): ~p",[L5]),
+ true = lists:prefix(Expected5,Str5),
+ true = L5<L4,
+ %% Check that neighbour information is printed
+ SplitFun = fun($;) -> false; (_) -> true end,
+ ExpectedNeighbours5 = "; neighbours: neighbour: pid: "++NPidStr++
+ ", registered_name: []",
+ true = lists:prefix(ExpectedNeighbours5,lists:dropwhile(SplitFun, Str5)),
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>500},
+ Str6 = flatten_report_cb(Report,FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "crasher: initial call: proc_lib_SUITE:sp2/0,",
+ ct:log("Str6: ~p",[Str6]),
+ ct:log("length(Str6): ~p",[L6]),
+ true = lists:prefix(Expected6,Str6),
+ true = L6<L4,
+ %% Check that only pid is printed for neighbour, due to chars_limit
+ ExpectedNeighbours6 = "; neighbours: ["++NPidStr++"]",
+ ExpectedNeighbours6 = lists:dropwhile(SplitFun, Str6),
+
+ ok = logger:remove_handler(?MODULE),
+ ok.
+
+report_cb_chars_limit(_Config) ->
+ %% This test does not really test anything, it just formats the
+ %% crash reports with different settings and prints the result. It
+ %% could be used as an example if report_cb was to be modified
+ %% for better utilization of the available number of characters
+ %% according to the chars_limit setting.
+ %%
+ %% Currently, multi-line formatting with chars_limit=1024 gives
+ %% a final report of 1696 character. The excess is due to the fact
+ %% that io_lib_pretty counts non-white characters--the indentation
+ %% of the formatted exception is not counted.
+ %%
+ %% Single-line formatting with chars_limit=1024 gives a final
+ %% report of 1104 characters.
+ %%
+ %% Single-line formatting a fake report with chars_limit=1024 gives
+ %% a final report of 1024 characters.
+
+ ok = logger:add_handler(?MODULE,?MODULE,#{config=>self()}),
+ Pid = proc_lib:spawn(?MODULE, rcb_tester, []),
+ ct:sleep(500),
+ Pid ! die,
+ Report =
+ receive
+ {report,R} ->
+ R
+ after 5000 ->
+ ct:fail(no_report_received)
+ end,
+
+ ct:sleep(500), % To separate debug calls to erlang:display(), if any.
+ Str1 = flatten_report_cb(Report,#{}),
+ L1 = length(Str1),
+ ct:log("Multi-line, no size limit:~n~s",[Str1]),
+ ct:log("Length, multi-line, no size limit: ~p",[L1]),
+
+ ct:sleep(500),
+ FormatOpts2 = #{chars_limit=>1024},
+ Str2 = flatten_report_cb(Report,FormatOpts2),
+ L2 = length(Str2),
+ ct:log("Multi-line, chars_limit=1024:~n~s",[Str2]),
+ ct:log("Length, multi-line, chars_limit=1024: ~p",[L2]),
+
+ ct:sleep(500),
+ FormatOpts3 = #{single_line=>true, chars_limit=>1024},
+ Str3 = flatten_report_cb(Report,FormatOpts3),
+ L3 = length(Str3),
+ ct:log("Single-line, chars_limit=1024:~n~s",[Str3]),
+ ct:log("Length, single-line, chars_limit=1024: ~p",[L3]),
+
+ ct:sleep(500),
+ Seq = lists:seq(1, 1000),
+ FakeReport = [[{fake_tag,Seq}],Seq],
+ FReport = #{label=>{proc_lib,crash}, report=>FakeReport},
+ Str4 = flatten_report_cb(FReport,FormatOpts3),
+ L4 = length(Str4),
+ ct:log("Fake: Single-line, chars_limit=1024:~n~s",[Str4]),
+ ct:log("Fake: Length, single-line, chars_limit=1024: ~p",[L4]),
+
+ ok = logger:remove_handler(?MODULE),
+ ok.
+
+rcb_tester() ->
+ L = lists:seq(1,255),
+ Term = [{some_data,#{pids=>processes(),
+ info=>process_info(self())}},
+ {tabs,lists:sort(ets:all())},
+ {bin,list_to_binary(L)},
+ {list,L}],
+
+ %% Put something in process dictionary
+ [put(K,V) ||{K,V} <- Term],
+
+ %% Add some messages
+ [self() ! {some_message,T} || T <- Term],
+
+ %% Create some neighbours
+ [_ = proc_lib:spawn_link(?MODULE,sp1,[]) || _ <- lists:seq(1,5)],
+
+ receive
+ die -> error({badmatch,Term})
+ end.
+
+flatten_report_cb(Report, Format) ->
+ lists:flatten(proc_lib:report_cb(Report, Format)).
+
%%-----------------------------------------------------------------
%% The error_logger handler used.
%%-----------------------------------------------------------------
@@ -648,3 +994,11 @@ handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
terminate(_Reason, State) ->
State.
+
+%%-----------------------------------------------------------------
+%% The Logger handler used.
+%%-----------------------------------------------------------------
+log(#{msg:={report,Report}},#{config:=Pid}) ->
+ Pid ! {report,Report};
+log(_,_) ->
+ ok.
diff --git a/lib/stdlib/test/property_test/shell_docs_prop.erl b/lib/stdlib/test/property_test/shell_docs_prop.erl
new file mode 100644
index 0000000000..83b62e8837
--- /dev/null
+++ b/lib/stdlib/test/property_test/shell_docs_prop.erl
@@ -0,0 +1,135 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(shell_docs_prop).
+-export([prop_render/0]).
+
+%%% This will include the .hrl file for the installed testing tool:
+-include_lib("common_test/include/ct_property_test.hrl").
+-include_lib("kernel/include/eep48.hrl").
+-compile([export_all]).
+
+prop_render() ->
+ numtests(10000,
+ ?FORALL(Doc,
+ ?LET(Blocks,blocks(),
+ #docs_v1{ module_doc = #{ <<"en">> => Blocks }, docs = [] }
+ ),
+ begin
+ try
+ shell_docs:render(test, Doc),
+ shell_docs:validate(Doc),
+ N1 = shell_docs:normalize(maps:get(<<"en">>,Doc#docs_v1.module_doc)),
+ N1 = shell_docs:normalize(N1),
+ true
+ catch C:E:ST ->
+ ct:pal("~p ~p:~p ~p~n",[Doc,C,E,ST]),
+ false
+ end
+ end)).
+
+-define(LIMIT,5).
+
+blocks() ->
+ blocks([]).
+
+blocks([l|_] = S) ->
+ ?LAZY(
+ frequency(
+ [{2,[]},
+ {2,[{li,[],blocks([li | S])}]}])
+ );
+blocks([dl|_] = S) ->
+ ?LAZY(
+ frequency(
+ [{2,[]},
+ {2,[{dt,[],blocks([dt | S])}]},
+ {2,[{dd,[],blocks([dd | S])}]}])
+ );
+blocks(S) ->
+ ?LAZY(
+ frequency(
+ [{?LIMIT div 2,[]},
+ {max(1,?LIMIT - length(S)),
+ ?LET(Lst,[block(S)|blocks(S)],lists:flatten(Lst))},
+ {max(1,?LIMIT - length(S)),
+ ?LET(Lst,[inlines()|blocks(S)],lists:flatten(Lst))}
+ ]
+ )).
+
+inlines() ->
+ inlines([]).
+inlines(S) ->
+ ?LAZY(
+ frequency(
+ [{?LIMIT,[]},
+ {max(1,?LIMIT - length(S)),[inline(S)|inlines(S)]}
+ ]
+ )).
+
+block(S) ->
+ frequency(
+ fmax('div',3,S,{'div',oneof([[],[{class,<<"Warning">>}]]), blocks(['div'|S])}) ++
+ fmax(p,1,S,{p,[],blocks([p|S])}) ++
+ fmax(l,3,S,{ul,[],blocks([l|S])}) ++
+ fmax(l,3,S,{ol,[],blocks([l|S])}) ++
+ fmax(dl,3,S,{dl,[],blocks([dl|S])}) ++
+ fmax(['div',l,dl],0,S,{h1,[],inlines(['div'|S])}) ++
+ fmax(['div',l,dl],0,S,{h2,[],inlines(['div'|S])}) ++
+ fmax(['div',l,dl],1,S,{h3,[],inlines(['div'|S])}) ++
+ [{5,{br,[],[]}}]
+ ).
+
+inline(S) ->
+ frequency(
+ fmax(i,1,S,{i,[],?LAZY(inlines([i|S]))}) ++
+ fmax(code,1,S,{code,[],?LAZY(inlines([code|S]))}) ++
+ fmax(a,1,S,{a,[],?LAZY(inlines([a|S]))}) ++
+ fmax(em,1,S,{em,[],?LAZY(inlines([em|S]))}) ++
+ [{10,characters()}]).
+
+characters() ->
+ ?LET(Str,list(frequency([{10,printable_character()},{1,char()}])),
+ unicode:characters_to_binary(Str)).
+
+printable_character() ->
+ oneof([integer($\040,$\176),
+ integer(16#A0, 16#D800-1),
+ integer(16#DFFF+1,16#FFFE-1),
+ integer(16#FFFF+1,16#10FFFF),
+ $\n,$\r,$\t,$\v,$\b,$\f,$\e]).
+
+fmax(What,Depth,S,E) when not is_list(What) ->
+ fmax([What],Depth,S,E);
+fmax(What,Depth,S,E) ->
+ Cnt =
+ lists:foldl(
+ fun(E,Cnt) ->
+ case lists:member(E,What) of
+ true ->
+ Cnt+1;
+ false ->
+ Cnt
+ end
+ end, 0, S),
+ if Depth-Cnt =< 0 ->
+ [];
+ true ->
+ [{10 - (Depth-Cnt),E}]
+ end.
diff --git a/lib/stdlib/test/property_test/uri_string_recompose.erl b/lib/stdlib/test/property_test/uri_string_recompose.erl
index 39fadf23c2..3c0dae0f8b 100644
--- a/lib/stdlib/test/property_test/uri_string_recompose.erl
+++ b/lib/stdlib/test/property_test/uri_string_recompose.erl
@@ -85,9 +85,12 @@ prop_recompose() ->
prop_normalize() ->
?FORALL(Map, map(),
- uri_string:normalize(Map, [return_map]) =:=
- uri_string:normalize(uri_string:parse(uri_string:recompose(Map)),
- [return_map])).
+ uri_string:percent_decode(
+ uri_string:normalize(Map, [return_map])) =:=
+ uri_string:percent_decode(
+ uri_string:normalize(
+ uri_string:parse(uri_string:recompose(Map)),
+ [return_map]))).
%% Stats
prop_map_key_length_collect() ->
diff --git a/lib/stdlib/test/re_SUITE_data/testoutput2 b/lib/stdlib/test/re_SUITE_data/testoutput2
index 4ccda27201..f5d32d6ad0 100644
--- a/lib/stdlib/test/re_SUITE_data/testoutput2
+++ b/lib/stdlib/test/re_SUITE_data/testoutput2
@@ -5614,9 +5614,8 @@ No match
123456\P
No match
-//KF>testsavedregex
+//S-KF>testsavedregex
Compiled pattern written to testsavedregex
-Study data written to testsavedregex
/abc/IS>testsavedregex
Capturing subpattern count = 0
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index cdb6031b07..4df0a2238a 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -64,8 +64,9 @@ end_per_testcase(_Case, Config) ->
OrigPath = proplists:get_value(orig_path,Config),
code:set_path(OrigPath),
application:unset_env(stdlib, restricted_shell),
- (catch code:purge(user_default)),
- (catch code:delete(user_default)),
+ purge_and_delete(user_default),
+ %% used by `records' test case
+ purge_and_delete(test),
ok.
-endif.
@@ -298,8 +299,7 @@ restricted_local(Config) when is_list(Config) ->
comm_err(<<"begin shell:stop_restricted() end.">>),
undefined =
application:get_env(stdlib, restricted_shell),
- (catch code:purge(user_default)),
- true = (catch code:delete(user_default)),
+ true = purge_and_delete(user_default),
ok.
@@ -428,6 +428,30 @@ records(Config) when is_list(Config) ->
[{error,invalid_filename}] = scan(<<"rr({foo}).">>),
[[]] = scan(<<"rr(\"not_a_file\").">>),
+ %% load record from archive
+ true = purge_and_delete(test),
+
+ PrivDir = proplists:get_value(priv_dir, Config),
+ AppDir = filename:join(PrivDir, "test_app"),
+ ok = file:make_dir(AppDir),
+ AppEbinDir = filename:join(AppDir, "ebin"),
+ ok = file:make_dir(AppEbinDir),
+
+ ok = file:write_file(Test, Contents),
+ {ok, test} = compile:file(Test, [{outdir, AppEbinDir}]),
+
+ Ext = init:archive_extension(),
+ Archive = filename:join(PrivDir, "test_app" ++ Ext),
+ {ok, _} = zip:create(Archive, ["test_app"], [{compress, []}, {cwd, PrivDir}]),
+
+ ArchiveEbinDir = filename:join(Archive, "test_app/ebin"),
+ true = code:add_path(ArchiveEbinDir),
+ {module, test} = code:load_file(test),
+ BeamInArchive = filename:join(ArchiveEbinDir, "test.beam"),
+ BeamInArchive = code:which(test),
+
+ [[state]] = scan(<<"rr(test).">>),
+
%% using records
[2] = scan(<<"rd(foo,{bar}), record_info(size, foo).">>),
[true] = scan(<<"rd(foo,{bar}), is_record(#foo{}, foo).">>),
@@ -601,7 +625,7 @@ otp_5327(Config) when is_list(Config) ->
comm_err(<<"<<103133:64/binary>> = <<103133:64/float>>.">>),
"exception error: interpreted function with arity 1 called with two arguments" =
comm_err(<<"(fun(X) -> X end)(a,b).">>),
- {'EXIT', {{illegal_pattern,_}, _}} =
+ {'EXIT', {{badmatch,<<17:32>>}, _}} =
(catch evaluate("<<A:a>> = <<17:32>>.", [])),
C = <<"
<<A:4,B:4,C:4,D:4,E:4,F:4>> = <<\"hej\">>,
@@ -614,6 +638,9 @@ otp_5327(Config) when is_list(Config) ->
%% unbound_var would be nicer...
{'EXIT',{{illegal_pattern,_},_}} =
(catch evaluate(<<"<<A:B>> = <<17:32>>.">>, [])),
+ %% A badarith exception is turned into badmatch.
+ {'EXIT', {{badmatch,<<1777:32>>}, _}} =
+ (catch evaluate(<<"<<A:(1/0)>> = <<1777:32>>.">>, [])),
%% undefined_bittype is turned into badmatch:
{'EXIT',{{badmatch,<<17:32>>},_}} =
(catch evaluate(<<"<<A/apa>> = <<17:32>>.">>, [])),
@@ -2941,7 +2968,7 @@ otp_14296(Config) when is_list(Config) ->
end(),
fun() ->
- Port = open_port({spawn, "ls"}, [{line,1}]),
+ Port = open_port({spawn, "erl -s erlang halt"}, [{line,1}]),
KnownPort = erlang:port_to_list(Port),
S = KnownPort ++ ".",
R = KnownPort ++ ".\n",
@@ -3141,25 +3168,16 @@ io_request({get_geometry,columns}, S) ->
{ok,80,S};
io_request({get_geometry,rows}, S) ->
{ok,24,S};
-io_request({put_chars,Chars}, S) ->
- {ok,ok,S#state{reply = [S#state.reply | Chars]}};
io_request({put_chars,latin1,Chars}, S) ->
{ok,ok,S#state{reply = [S#state.reply | Chars]}};
io_request({put_chars,unicode,Chars0}, S) ->
Chars = unicode:characters_to_list(Chars0),
{ok,ok,S#state{reply = [S#state.reply | Chars]}};
-io_request({put_chars,Mod,Func,Args}, S) ->
- case catch apply(Mod, Func, Args) of
- Chars when is_list(Chars) ->
- io_request({put_chars,Chars}, S)
- end;
io_request({put_chars,Enc,Mod,Func,Args}, S) ->
case catch apply(Mod, Func, Args) of
Chars when is_list(Chars) ->
io_request({put_chars,Enc,Chars}, S)
end;
-io_request({get_until,_Prompt,Mod,Func,ExtraArgs}, S) ->
- get_until(Mod, Func, ExtraArgs, S, latin1);
io_request({get_until,Enc,_Prompt,Mod,Func,ExtraArgs}, S) ->
get_until(Mod, Func, ExtraArgs, S, Enc).
@@ -3224,3 +3242,6 @@ start_node(Name, Xargs) ->
global:sync(),
N.
+purge_and_delete(Module) ->
+ (catch code:purge(Module)),
+ (catch code:delete(Module)).
diff --git a/lib/stdlib/test/shell_docs_SUITE.erl b/lib/stdlib/test/shell_docs_SUITE.erl
new file mode 100644
index 0000000000..222aec5e0b
--- /dev/null
+++ b/lib/stdlib/test/shell_docs_SUITE.erl
@@ -0,0 +1,237 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2020. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(shell_docs_SUITE).
+-export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2]).
+
+-export([render/1, links/1, normalize/1, render_prop/1]).
+
+-export([render_all/1]).
+
+-include_lib("kernel/include/eep48.hrl").
+-include_lib("stdlib/include/assert.hrl").
+
+suite() ->
+ [{timetrap,{minutes,10}}].
+
+all() ->
+ [render, links, normalize, {group, prop}].
+
+groups() ->
+ [{prop,[],[render_prop]}].
+
+%% Include a spec here in order to test that specs of undocumented functions
+%% is rendered correctly.
+-spec init_per_suite(Config1) -> Config2 when
+ Config1 :: list({atom(),term()}),
+ Config2 :: list({atom(),term()}).
+init_per_suite(Config) ->
+ {ok, ?MODULE} = c:c(?MODULE,[debug_info]),
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(prop, Config) ->
+ ct_property_test:init_per_suite(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+render(_Config) ->
+ docsmap(
+ fun(Mod, #docs_v1{ docs = Docs } = D) ->
+ lists:foreach(
+ fun(Config) ->
+ try
+ shell_docs:render(Mod, D, Config),
+ shell_docs:render_type(Mod, D, Config),
+ shell_docs:render_callback(Mod, D, Config),
+ [try
+ shell_docs:render(Mod, F, A, D, Config)
+ catch _E:R:ST ->
+ io:format("Failed to render ~p:~p/~p~n~p:~p~n~p~n",
+ [Mod,F,A,R,ST,shell_docs:get_doc(Mod,F,A)]),
+ erlang:raise(error,R,ST)
+ end || {F,A} <- Mod:module_info(exports)],
+ [try
+ shell_docs:render_type(Mod, T, A, D, Config)
+ catch _E:R:ST ->
+ io:format("Failed to render type ~p:~p/~p~n~p:~p~n~p~n",
+ [Mod,T,A,R,ST,shell_docs:get_type_doc(Mod,T,A)]),
+ erlang:raise(error,R,ST)
+ end || {{type,T,A},_,_,_,_} <- Docs],
+ [try
+ shell_docs:render_callback(Mod, T, A, D, Config)
+ catch _E:R:ST ->
+ io:format("Failed to render callback ~p:~p/~p~n~p:~p~n~p~n",
+ [Mod,T,A,R,ST,shell_docs:get_callback_doc(Mod,T,A)]),
+ erlang:raise(error,R,ST)
+ end || {{callback,T,A},_,_,_,_} <- Docs]
+ catch throw:R:ST ->
+ io:format("Failed to render ~p~n~p:~p~n",[Mod,R,ST]),
+ exit(R)
+ end
+ end, [#{},
+ #{ ansi => false },
+ #{ ansi => true },
+ #{ columns => 5 },
+ #{ columns => 150 },
+ #{ encoding => unicode},
+ #{ encoding => latin1}])
+ end),
+ ok.
+
+render_prop(Config) ->
+% dbg:tracer(),dbg:p(all,c),dbg:tpl(shell_docs_prop,[]),
+ ct_property_test:quickcheck(
+ shell_docs_prop:prop_render(),Config).
+
+links(_Config) ->
+ docsmap(
+ fun(Mod, #docs_v1{ module_doc = MDoc, docs = Docs }) ->
+ try
+ [check_links(Mod, maps:get(<<"en">>,MDoc)) || MDoc =/= none, MDoc =/= hidden]
+ catch _E1:R1:ST1 ->
+ io:format("Failed to render ~p~n~p:~p~n~p~n",
+ [Mod,R1,ST1,MDoc]),
+ erlang:raise(error,R1,ST1)
+ end,
+ [try
+ check_links(Mod, D)
+ catch _E:R:ST ->
+ io:format("Failed to render ~p:~p~n~p:~p~n~p~n",
+ [Mod,Kind,R,ST,D]),
+ erlang:raise(error,R,ST)
+ end || {Kind,_Anno,_Sig,#{ <<"en">> := D },_MD} <- Docs]
+ end).
+
+check_links(Mod, [{a,Attr,C}|T]) ->
+ case proplists:get_value(rel,Attr) of
+ <<"https://erlang.org/doc/link/seemfa">> ->
+ case string:lexemes(proplists:get_value(href, Attr),":#/") of
+ [_App,TargetMod,Func,Arity] ->
+ case code:get_doc(b2a(TargetMod)) of
+ {ok, _ModDoc} ->
+ case shell_docs:get_doc(b2a(TargetMod),
+ b2a(Func),
+ binary_to_integer(Arity)) of
+ [] -> throw({could_not_find,b2a(TargetMod),
+ b2a(Func),
+ binary_to_integer(Arity)});
+ _Else -> ok
+ end;
+ _Else ->
+ throw({could_not_find, TargetMod, Attr})
+ end;
+ _Callback ->
+ ok
+ end;
+ _OtherlinkType ->
+ ok
+ end,
+ check_links(Mod, C),
+ check_links(Mod, T);
+check_links(Mod, [{_,_,C}|T]) ->
+ check_links(Mod, C),
+ check_links(Mod, T);
+check_links(Mod, [C|T]) when is_binary(C) ->
+ check_links(Mod, T);
+check_links(_, []) ->
+ ok.
+
+normalize(_Config) ->
+ ?assertMatch(
+ [{p,[],[{em,[],[<<"a ">>,{code,[],[<<"b ">>]},<<"c">>]}]}],
+ shell_docs:normalize([{p,[],[{em,[],[<<" a ">>,{code,[],[<<" b ">>]},<<" c">>]}]}])
+ ),
+ ?assertMatch(
+ [{'div',[],[<<"!">>]}],
+ shell_docs:normalize([{'div',[],[{code,[],[<<" ">>,{i,[],[<<" ">>]}]},<<" !">>]}])
+ ),
+ ok.
+
+%% Special binary_to_atom that deals with <<"'and'">>
+b2a(Bin) ->
+ case erl_scan:string(binary_to_list(Bin)) of
+ {ok,[{atom,_,A}],_} -> A;
+ {ok,[{A,_}],_} -> A
+ end.
+
+%% Testing functions
+render_all(Dir) ->
+ file:make_dir(Dir),
+ docsmap(
+ fun(Mod, #docs_v1{ docs = Docs } = D) ->
+ SMod = atom_to_list(Mod),
+ file:write_file(filename:join(Dir,SMod ++ ".txt"),
+ unicode:characters_to_binary(shell_docs:render(Mod, D))),
+ file:write_file(filename:join(Dir,SMod ++ "_type.txt"),
+ unicode:characters_to_binary(shell_docs:render_type(Mod, D))),
+ file:write_file(filename:join(Dir,SMod ++ "_cb.txt"),
+ unicode:characters_to_binary(shell_docs:render_callback(Mod, D))),
+ lists:foreach(
+ fun({{function,Name,Arity},_Anno,_Sig,_Doc,_Meta}) ->
+ FName = SMod ++ "_"++atom_to_list(Name)++"_"++integer_to_list(Arity)++"_func.txt",
+ ok = file:write_file(filename:join(Dir,re:replace(FName,"[/:]","_",
+ [global,{return,list}])),
+ unicode:characters_to_binary(shell_docs:render(Mod, Name, Arity, D)));
+ ({{type,Name,Arity},_Anno,_Sig,_Doc,_Meta}) ->
+ FName = SMod ++ "_"++atom_to_list(Name)++"_"++integer_to_list(Arity)++"_type.txt",
+ ok = file:write_file(filename:join(Dir,re:replace(FName,"[/:]","_",
+ [global,{return,list}])),
+ unicode:characters_to_binary(shell_docs:render_type(Mod, Name, Arity, D)));
+ ({{callback,Name,Arity},_Anno,_Sig,_Doc,_Meta}) ->
+ FName = SMod ++ "_"++atom_to_list(Name)++"_"++integer_to_list(Arity)++"_cb.txt",
+ file:write_file(filename:join(Dir,re:replace(FName,"[/:]","_",
+ [global,{return,list}])),
+ unicode:characters_to_binary(shell_docs:render_callback(Mod, Name, Arity, D)))
+ end, Docs)
+ end).
+
+docsmap(Fun) ->
+ lists:map(
+ fun F({Mod,_,_}) ->
+ F(Mod);
+ F(Mod) when is_list(Mod) ->
+ F(list_to_atom(Mod));
+ F(Mod) ->
+ case code:get_doc(Mod) of
+ {error, missing} ->
+ ok;
+ {error, cover_compiled} ->
+ ok;
+ {error, E} when E =:= eperm; E =:= eacces ->
+ %% This can happen in BSD's for some reason...
+ ok;
+ {error, eisdir} ->
+ %% Uhm?
+ ok;
+ {ok, Docs} ->
+ try
+ Fun(Mod, Docs)
+ catch E:R:ST ->
+ io:format("Failed to render ~p~n~p:~p:~p~n",[Mod,E,R,ST]),
+ erlang:raise(E,R,ST)
+ end
+ end
+ end, code:all_available()).
diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl
index 029587a216..7e05723036 100644
--- a/lib/stdlib/test/stdlib_SUITE.erl
+++ b/lib/stdlib/test/stdlib_SUITE.erl
@@ -27,6 +27,8 @@
init_per_testcase/2, end_per_testcase/2,
app_test/1, appup_test/1, assert_test/1]).
+-compile(r21).
+
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
@@ -95,7 +97,7 @@ appup_tests(App,{OkVsns0,NokVsns}) ->
create_test_vsns(App) ->
ThisMajor = erlang:system_info(otp_release),
FirstMajor = previous_major(ThisMajor),
- SecondMajor = previous_major(FirstMajor),
+ SecondMajor = previous_major(previous_major(FirstMajor)),
Ok = app_vsn(App,[ThisMajor,FirstMajor]),
Nok0 = app_vsn(App,[SecondMajor]),
Nok = case Ok of
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index c9aadd7f10..4475d7c06e 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -1111,12 +1111,22 @@ needs_check(_) -> true.
%%%% Timer stuff
time_func(Fun, Mode, Bin, Repeat) ->
- timer:sleep(100), %% Let emulator catch up and clean things before test runs
+ %% Let emulator catch up and clean things before test runs
+ timer:sleep(100),
+
+ %% We're spawning with a minimum heap size of 1k because certain benchmarks
+ %% (e.g. string_lexemes_binary) keep very little heap data alive, making
+ %% them very sensitive to changes in GC behavior.
+ %%
+ %% If we don't do this, something as benign as shrinking a stack frame can
+ %% make things run slower by making it stick to a smaller heap size,
+ %% causing it to GC more often.
Self = self(),
- Pid = spawn_link(fun() ->
- Str = mode(Mode, Bin),
- Self ! {self(),time_func(0,0,0, Fun, Str, undefined, Repeat)}
- end),
+ Pid = spawn_opt(fun() ->
+ Str = mode(Mode, Bin),
+ Res = time_func(0,0,0, Fun, Str, undefined, Repeat),
+ Self ! {self(), Res}
+ end, [link, {min_heap_size, 1 bsl 10}]),
receive {Pid,Msg} -> Msg end.
time_func(N,Sum,SumSq, Fun, Str, _, Repeat) when N < Repeat ->
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 47df11923c..9d7ed2829a 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -63,6 +63,7 @@
one_for_one_escalation/1, one_for_all/1,
one_for_all_escalation/1, one_for_all_other_child_fails_restart/1,
simple_one_for_one/1, simple_one_for_one_escalation/1,
+ simple_one_for_one_corruption/1,
rest_for_one/1, rest_for_one_escalation/1,
rest_for_one_other_child_fails_restart/1,
simple_one_for_one_extra/1, simple_one_for_one_shutdown/1]).
@@ -77,7 +78,8 @@
hanging_restart_loop_rest_for_one/1,
hanging_restart_loop_simple/1, code_change/1, code_change_map/1,
code_change_simple/1, code_change_simple_map/1,
- order_of_children/1, scale_start_stop_many_children/1]).
+ order_of_children/1, scale_start_stop_many_children/1,
+ format_log_1/1, format_log_2/1]).
%%-------------------------------------------------------------------------
@@ -104,7 +106,8 @@ all() ->
simple_global_supervisor, hanging_restart_loop,
hanging_restart_loop_rest_for_one, hanging_restart_loop_simple,
code_change, code_change_map, code_change_simple, code_change_simple_map,
- order_of_children, scale_start_stop_many_children].
+ order_of_children, scale_start_stop_many_children,
+ format_log_1, format_log_2].
groups() ->
[{sup_start, [],
@@ -137,7 +140,8 @@ groups() ->
one_for_all_other_child_fails_restart]},
{restart_simple_one_for_one, [],
[simple_one_for_one, simple_one_for_one_shutdown,
- simple_one_for_one_extra, simple_one_for_one_escalation]},
+ simple_one_for_one_extra, simple_one_for_one_escalation,
+ simple_one_for_one_corruption]},
{restart_rest_for_one, [],
[rest_for_one, rest_for_one_escalation,
rest_for_one_other_child_fails_restart]}].
@@ -1276,6 +1280,62 @@ simple_one_for_one_extra(Config) when is_list(Config) ->
check_exit([SupPid]).
%%-------------------------------------------------------------------------
+%% Test for subtle corruption of internal state for a
+%% simple_one_for_one supervisor. Thanks to Zeyu Zhang (@zzydxm) and
+%% Maxim Fedorov for noticing this bug. (OTP-16804)
+simple_one_for_one_corruption(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+
+ logger:add_handler(?MODULE, ?MODULE, #{test_case_pid => self()}),
+
+ try
+ Child = #{id => child, start => {supervisor_1, start_child, []},
+ restart => temporary, shutdown => 1000,
+ type => worker, modules => []},
+ {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}),
+ {ok, CPid1} = supervisor:start_child(sup_test, []),
+
+ terminate(SupPid, CPid1, child1, abnormal),
+
+ %% The first time a child of simple_one_for_one supervisor
+ %% with restart strategy `temporary` dies, the internal state
+ %% for the supervisor will become inconsistent (the `dynamics`
+ %% field would change from `{mapsets,Map}` to
+ %% `{maps,Map}`). That inconsistency will make the supervisor
+ %% retain the start arguments even for temporary processes.
+ %%
+ %% To test that the bug is fixed, start a new child process
+ %% with a large term in its argument list.
+
+ N = 50000,
+ BigData = binary_to_list(<<0:N/unit:8>>),
+ {ok, CPid2, BigData} = supervisor:start_child(sup_test, [BigData]),
+
+ %% Since the child is temporary, the supervisor should not keep
+ %% the argument list for the child and the supervisor's heap
+ %% size should shrink after a GC.
+
+ true = erlang:garbage_collect(SupPid),
+ {total_heap_size, HeapSize} = process_info(SupPid, total_heap_size),
+ if
+ HeapSize > 2*N ->
+ %% The start arguments for the child have been kept.
+ ct:fail({excessive_heap_size,HeapSize});
+ true ->
+ ok
+ end,
+
+ terminate(SupPid, CPid2, child2, abnormal),
+
+ exit(SupPid, kill)
+ after
+ logger:remove_handler(?MODULE)
+ end,
+
+ ok.
+
+
+%%-------------------------------------------------------------------------
%% Test restart escalation on a simple_one_for_one supervisor.
simple_one_for_one_escalation(Config) when is_list(Config) ->
process_flag(trap_exit, true),
@@ -2379,11 +2439,21 @@ scale_start_stop_many_children() ->
ct:log("~w children, start time: ~w ms, stop time: ~w ms",
[N2, StartT2 div 1000, StopT2 div 1000]),
+ TimerAdjustment =
+ case os:type() of
+ {win32,_} ->
+ %% Windows timer unit is really bad...
+ 16000;
+ _ ->
+ %% To avoid div by zero
+ 1
+ end,
+
%% Scaling should be more or less linear, but allowing a bit more
%% to avoid false alarms (add 1 to avoid div zero)
ScaleLimit = (N2 div N1) * 10,
- StartScale = StartT2 div (StartT1+1),
- StopScale = StopT2 div (StopT1+1),
+ StartScale = StartT2 div (StartT1+TimerAdjustment),
+ StopScale = StopT2 div (StopT1+TimerAdjustment),
ct:log("Scale limit: ~w~nStart scale: ~w~nStop scale: ~w",
[ScaleLimit, StartScale, StopScale]),
@@ -2398,6 +2468,249 @@ scale_start_stop_many_children() ->
ok.
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1, 15),
+ Supervisor = my_supervisor,
+ Name = self(),
+ Error = shutdown_error,
+ Child = [{pid,Name},{id,any_id},
+ {mfargs,{mod,func,[any,Term]}},
+ {restart_type,temporary},
+ {shutdown,brutal_kill},
+ {child_type,worker}],
+ Report = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Child}]},
+ {F1, A1} = supervisor:format_log(Report),
+ FExpected1 = " supervisor: ~tp~n"
+ " errorContext: ~tp~n"
+ " reason: ~tp~n"
+ " offender: ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp", [F1,A1]),
+ FExpected1 = F1,
+ [Supervisor,Error,Term,Child] = A1,
+
+ Progress = #{label=>{supervisor,progress},
+ report=>[{supervisor,Supervisor},{started,Child}]},
+ {PF1,PA1} = supervisor:format_log(Progress),
+ PFExpected1 = " supervisor: ~tp~n started: ~tp~n",
+ ct:log("PF1: ~ts~nPA1: ~tp", [PF1,PA1]),
+ PFExpected1 = PF1,
+ [Supervisor,Child] = PA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = supervisor:format_log(Report),
+ FExpected2 = " supervisor: ~tP~n"
+ " errorContext: ~tP~n"
+ " reason: ~tP~n"
+ " offender: ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp", [F2,A2]),
+ FExpected2 = F2,
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ LimitedChild = [{pid,Name},{id,any_id},
+ {mfargs,{mod,func,[any,'...']}},
+ {restart_type,temporary},
+ {shutdown,brutal_kill},
+ {child_type,worker}],
+
+ [Supervisor,Depth,Error,Depth,Limited,Depth,LimitedChild,Depth] = A2,
+
+ {PF2,PA2} = supervisor:format_log(Progress),
+ PFExpected2 = " supervisor: ~tP~n started: ~tP~n",
+ ct:log("PF2: ~ts~nPA2: ~tp", [PF2,PA2]),
+ PFExpected2 = PF2,
+ [Supervisor,Depth,LimitedChild,Depth] = PA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1, 15),
+ Supervisor = my_supervisor,
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Error = shutdown_error,
+ Child = [{pid,Name},{id,any_id},
+ {mfargs,{mod,func,[Term]}},
+ {restart_type,temporary},
+ {shutdown,brutal_kill},
+ {child_type,worker}],
+ Report = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Child}]},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ " offender: [{pid,"++NameStr++"},\n"
+ " {id,any_id},\n"
+ " {mfargs,{mod,func,"
+ "[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]}},\n"
+ " {restart_type,temporary},\n"
+ " {shutdown,brutal_kill},\n"
+ " {child_type,worker}]\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = Expected1 =:= Str1,
+
+ Progress = #{label=>{supervisor,progress},
+ report=>[{supervisor,Supervisor},{started,Child}]},
+ PStr1 = flatten_format_log(Progress, FormatOpts1),
+ PL1 = length(PStr1),
+ PExpected1 = " supervisor: my_supervisor\n"
+ " started: [{pid,"++NameStr++"},\n"
+ " {id,any_id},\n"
+ " {mfargs,{mod,func,"
+ "[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]}},\n"
+ " {restart_type,temporary},\n"
+ " {shutdown,brutal_kill},\n"
+ " {child_type,worker}]\n",
+ ct:log("PStr1: ~ts", [PStr1]),
+ ct:log("length(PStr1): ~p", [PL1]),
+ true = PExpected1 =:= PStr1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9|...]\n"
+ " offender: [{pid,"++NameStr++"},\n"
+ " {id,any_id},\n"
+ " {mfargs,{mod,func,[[...]]}},\n"
+ " {restart_type,temporary},\n"
+ " {shutdown,brutal_kill},\n"
+ " {child_type,worker}]\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ PStr2 = flatten_format_log(Progress, FormatOpts2),
+ PL2 = length(PStr2),
+ PExpected2 = " supervisor: my_supervisor\n"
+ " started: [{pid,"++NameStr++"},\n"
+ " {id,any_id},\n"
+ " {mfargs,{mod,func,[[...]]}},\n"
+ " {restart_type,temporary},\n"
+ " {shutdown,brutal_kill},\n"
+ " {child_type,worker}]\n",
+ ct:log("PStr2: ~ts", [PStr2]),
+ ct:log("length(PStr2): ~p", [PL2]),
+ true = PExpected2 =:= PStr2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = " supervisor: my_supervisor\n"
+ " errorContext: ",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = lists:prefix(Expected3, Str3),
+ true = L3 < L1,
+
+ PFormatOpts3 = #{chars_limit=>80},
+ PStr3 = flatten_format_log(Progress, PFormatOpts3),
+ PL3 = length(PStr3),
+ PExpected3 = " supervisor: my_supervisor\n started:",
+ ct:log("PStr3: ~ts", [PStr3]),
+ ct:log("length(PStr3): ~p", [PL3]),
+ true = lists:prefix(PExpected3, PStr3),
+ true = PL3 < PL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+
+ Expected4 = "Supervisor: my_supervisor. Context: shutdown_error. "
+ "Reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Offender: id=any_id,pid="++NameStr++".",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ true = Expected4 =:= Str4,
+
+ PStr4 = flatten_format_log(Progress, FormatOpts4),
+ PL4 = length(PStr4),
+ PExpected4 = "Supervisor: my_supervisor. "
+ "Started: id=any_id,pid="++NameStr++".",
+ ct:log("PStr4: ~ts", [PStr4]),
+ ct:log("length(PStr4): ~p", [PL4]),
+ true = PExpected4 =:= PStr4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Supervisor: my_supervisor. Context: shutdown_error. "
+ "Reason: [1,2,3,4,5,6,7,8,9|...]. "
+ "Offender: id=any_id,pid="++NameStr++".",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ true = Expected5 =:= Str5,
+
+ PStr5 = flatten_format_log(Progress, FormatOpts5),
+ PL5 = length(PStr5),
+ PExpected5 = "Supervisor: my_supervisor. "
+ "Started: id=any_id,pid="++NameStr++".",
+ ct:log("PStr5: ~ts", [PStr5]),
+ ct:log("length(PStr5): ~p", [PL5]),
+ true = PExpected5 =:= PStr5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>200},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Supervisor: my_supervisor. Context:",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ PFormatOpts6 = #{single_line=>true, chars_limit=>60},
+ PStr6 = flatten_format_log(Progress, PFormatOpts6),
+ PL6 = length(PStr6),
+ PExpected6 = "Supervisor: my_supervisor.",
+ ct:log("PStr6: ~ts", [PStr6]),
+ ct:log("length(PStr6): ~p", [PL6]),
+ true = lists:prefix(PExpected6, PStr6),
+ true = PL6 < PL4,
+
+ Child2 = [{nb_children,7},{id,any_id},
+ {mfargs,{mod,func,[Term]}},
+ {restart_type,temporary},
+ {shutdown,brutal_kill},
+ {child_type,worker}],
+ Report2 = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Child2}]},
+ Str7 = flatten_format_log(Report2, FormatOpts6),
+ L7 = length(Str7),
+ ct:log("Str7: ~ts", [Str7]),
+ ct:log("length(Str7): ~p", [L7]),
+ true = string:find(Str7, "Offender: id=any_id,nb_children=7.") =/= nomatch,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(supervisor:format_log(Report, Format)).
+
%%-------------------------------------------------------------------------
terminate(Pid, Reason) when Reason =/= supervisor ->
terminate(dummy, Pid, dummy, Reason).
diff --git a/lib/stdlib/test/supervisor_bridge_SUITE.erl b/lib/stdlib/test/supervisor_bridge_SUITE.erl
index 279ae91cdc..60af37dd9a 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-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,starting/1,
mini_terminate/1,mini_die/1,badstart/1,
- simple_global_supervisor/1]).
+ simple_global_supervisor/1, format_log_1/1, format_log_2/1]).
-export([client/1,init/1,internal_loop_init/1,terminate/2,server9212/0]).
-include_lib("common_test/include/ct.hrl").
@@ -35,7 +35,8 @@ suite() ->
{timetrap,{minutes,1}}].
all() ->
- [starting, mini_terminate, mini_die, badstart, simple_global_supervisor].
+ [starting, mini_terminate, mini_die, badstart, simple_global_supervisor,
+ format_log_1, format_log_2].
groups() ->
[].
@@ -227,3 +228,202 @@ simple_global_supervisor(Config) when is_list(Config) ->
server9212() ->
supervisor_bridge:start_link({global,?bridge_name}, ?MODULE, 3).
+
+%% Test report callback for Logger handler error_logger
+format_log_1(_Config) ->
+ FD = application:get_env(kernel, error_logger_format_depth),
+ application:unset_env(kernel, error_logger_format_depth),
+ Term = lists:seq(1, 15),
+ Supervisor = my_supervisor,
+ Name = self(),
+ Error = error,
+ Offender = [{pid,Name},{mod,a_module}],
+ Report = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Offender}]},
+ {F1, A1} = supervisor_bridge:format_log(Report),
+ FExpected1 = " supervisor: ~tp~n"
+ " errorContext: ~tp~n"
+ " reason: ~tp~n"
+ " offender: ~tp~n",
+ ct:log("F1: ~ts~nA1: ~tp", [F1,A1]),
+ FExpected1 = F1,
+ [Supervisor,Error,Term,Offender] = A1,
+
+ Started = [{pid,Name},{mfa,{a_module,init,[Term]}}],
+ Progress = #{label=>{supervisor,progress},
+ report=>[{supervisor,Supervisor},{started,Started}]},
+ {PF1,PA1} = supervisor_bridge:format_log(Progress),
+ PFExpected1 = " supervisor: ~tp~n started: ~tp~n",
+ ct:log("PF1: ~ts~nPA1: ~tp", [PF1,PA1]),
+ PFExpected1 = PF1,
+ [Supervisor,Started] = PA1,
+
+ Depth = 10,
+ ok = application:set_env(kernel, error_logger_format_depth, Depth),
+ Limited = [1,2,3,4,5,6,7,8,9,'...'],
+ {F2,A2} = supervisor_bridge:format_log(Report),
+ FExpected2 = " supervisor: ~tP~n"
+ " errorContext: ~tP~n"
+ " reason: ~tP~n"
+ " offender: ~tP~n",
+ ct:log("F2: ~ts~nA2: ~tp", [F2,A2]),
+ FExpected2 = F2,
+ LimitedOffender = Offender,
+ [Supervisor,Depth,Error,Depth,Limited,Depth,LimitedOffender,Depth] = A2,
+
+ LimitedStarted =
+ [{pid,Name},{mfa,{a_module,init,[[1,2,3,4,5,6,7,8,9,'...']]}}],
+ {PF2,PA2} = supervisor_bridge:format_log(Progress),
+ PFExpected2 = " supervisor: ~tP~n started: ~tP~n",
+ ct:log("PF2: ~ts~nPA2: ~tp", [PF2,PA2]),
+ PFExpected2 = PF2,
+ [Supervisor,Depth,LimitedStarted,Depth] = PA2,
+
+ case FD of
+ undefined ->
+ application:unset_env(kernel, error_logger_format_depth);
+ _ ->
+ application:set_env(kernel, error_logger_format_depth, FD)
+ end,
+ ok.
+
+%% Test report callback for any Logger handler
+format_log_2(_Config) ->
+ Term = lists:seq(1, 15),
+ Supervisor = my_supervisor,
+ Name = self(),
+ NameStr = pid_to_list(Name),
+ Error = shutdown_error,
+ Offender = [{pid,Name},{mod,a_module}],
+ Report = #{label=>{supervisor,Error},
+ report=>[{supervisor,Supervisor},
+ {errorContext,Error},
+ {reason,Term},
+ {offender,Offender}]},
+ FormatOpts1 = #{},
+ Str1 = flatten_format_log(Report, FormatOpts1),
+ L1 = length(Str1),
+ Expected1 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ " offender: [{pid,"++NameStr++"},{mod,a_module}]\n",
+ ct:log("Str1: ~ts", [Str1]),
+ ct:log("length(Str1): ~p", [L1]),
+ true = Expected1 =:= Str1,
+
+ Started = [{pid,Name},{mfa,{a_module,init,[Term]}}],
+ Progress = #{label=>{supervisor,progress},
+ report=>[{supervisor,Supervisor},{started,Started}]},
+ PStr1 = flatten_format_log(Progress, FormatOpts1),
+ PL1 = length(PStr1),
+ PExpected1 = " supervisor: my_supervisor\n"
+ " started: [{pid,"++NameStr++"},\n"
+ " {mfa,{a_module,init,"
+ "[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]}}]\n",
+ ct:log("PStr1: ~ts", [PStr1]),
+ ct:log("length(PStr1): ~p", [PL1]),
+ true = PExpected1 =:= PStr1,
+
+ Depth = 10,
+ FormatOpts2 = #{depth=>Depth},
+ Str2 = flatten_format_log(Report, FormatOpts2),
+ L2 = length(Str2),
+ Expected2 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9|...]\n"
+ " offender: [{pid,"++NameStr++"},{mod,a_module}]\n",
+ ct:log("Str2: ~ts", [Str2]),
+ ct:log("length(Str2): ~p", [L2]),
+ true = Expected2 =:= Str2,
+
+ PStr2 = flatten_format_log(Progress, FormatOpts2),
+ PL2 = length(PStr2),
+ PExpected2 = " supervisor: my_supervisor\n"
+ " started: [{pid,"++NameStr++"},"
+ "{mfa,{a_module,init,[[1|...]]}}]\n",
+ ct:log("PStr2: ~ts", [PStr2]),
+ ct:log("length(PStr2): ~p", [PL2]),
+ true = PExpected2 =:= PStr2,
+
+ FormatOpts3 = #{chars_limit=>200},
+ Str3 = flatten_format_log(Report, FormatOpts3),
+ L3 = length(Str3),
+ Expected3 = " supervisor: my_supervisor\n"
+ " errorContext: shutdown_error\n"
+ " reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]\n"
+ " offender: [{pid,"++NameStr++"},{mod,a_module}]\n",
+ ct:log("Str3: ~ts", [Str3]),
+ ct:log("length(Str3): ~p", [L3]),
+ true = Expected3 =:= Str3,
+
+ PFormatOpts3 = #{chars_limit=>80},
+ PStr3 = flatten_format_log(Progress, PFormatOpts3),
+ PL3 = length(PStr3),
+ PExpected3 = " supervisor: my_supervisor\n started:",
+ ct:log("PStr3: ~ts", [PStr3]),
+ ct:log("length(PStr3): ~p", [PL3]),
+ true = lists:prefix(PExpected3, PStr3),
+ true = PL3 < PL1,
+
+ FormatOpts4 = #{single_line=>true},
+ Str4 = flatten_format_log(Report, FormatOpts4),
+ L4 = length(Str4),
+ Expected4 = "Supervisor: my_supervisor. Context: shutdown_error. "
+ "Reason: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. "
+ "Offender: pid="++NameStr++",mod=a_module.",
+ ct:log("Str4: ~ts", [Str4]),
+ ct:log("length(Str4): ~p", [L4]),
+ true = Expected4 =:= Str4,
+
+ PStr4 = flatten_format_log(Progress, FormatOpts4),
+ PL4 = length(PStr4),
+ PExpected4 = "Supervisor: my_supervisor. "
+ "Started: pid="++NameStr++",mfa={a_module,init,"
+ "[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]}.",
+ ct:log("PStr4: ~ts", [PStr4]),
+ ct:log("length(PStr4): ~p", [PL4]),
+ true = PExpected4 =:= PStr4,
+
+ FormatOpts5 = #{single_line=>true, depth=>Depth},
+ Str5 = flatten_format_log(Report, FormatOpts5),
+ L5 = length(Str5),
+ Expected5 = "Supervisor: my_supervisor. Context: shutdown_error. "
+ "Reason: [1,2,3,4,5,6,7,8,9|...]. "
+ "Offender: pid="++NameStr++",mod=a_module.",
+ ct:log("Str5: ~ts", [Str5]),
+ ct:log("length(Str5): ~p", [L5]),
+ true = Expected5 =:= Str5,
+
+ PStr5 = flatten_format_log(Progress, FormatOpts5),
+ PL5 = length(PStr5),
+ PExpected5 = "Supervisor: my_supervisor. "
+ "Started: pid="++NameStr++",mfa={a_module,init,[[1,2,3,4,5|...]]}.",
+ ct:log("PStr5: ~ts", [PStr5]),
+ ct:log("length(PStr5): ~p", [PL5]),
+ true = PExpected5 =:= PStr5,
+
+ FormatOpts6 = #{single_line=>true, chars_limit=>100},
+ Str6 = flatten_format_log(Report, FormatOpts6),
+ L6 = length(Str6),
+ Expected6 = "Supervisor: my_supervisor. Context:",
+ ct:log("Str6: ~ts", [Str6]),
+ ct:log("length(Str6): ~p", [L6]),
+ true = lists:prefix(Expected6, Str6),
+ true = L6 < L4,
+
+ PFormatOpts6 = #{single_line=>true, chars_limit=>60},
+ PStr6 = flatten_format_log(Progress, PFormatOpts6),
+ PL6 = length(PStr6),
+ PExpected6 = "Supervisor: my_supervisor.",
+ ct:log("PStr6: ~ts", [PStr6]),
+ ct:log("length(PStr6): ~p", [PL6]),
+ true = lists:prefix(PExpected6, PStr6),
+ true = PL6 < PL4,
+
+ ok.
+
+flatten_format_log(Report, Format) ->
+ lists:flatten(supervisor_bridge:format_log(Report, Format)).
diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl
index 55af3b16b8..585894af86 100644
--- a/lib/stdlib/test/tar_SUITE.erl
+++ b/lib/stdlib/test/tar_SUITE.erl
@@ -28,7 +28,8 @@
extract_from_open_file/1, symlinks/1, open_add_close/1, cooked_compressed/1,
memory/1,unicode/1,read_other_implementations/1,
sparse/1, init/1, leading_slash/1, dotdot/1,
- roundtrip_metadata/1, apply_file_info_opts/1]).
+ roundtrip_metadata/1, apply_file_info_opts/1,
+ incompatible_options/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
@@ -43,7 +44,7 @@ all() ->
symlinks, open_add_close, cooked_compressed, memory, unicode,
read_other_implementations,
sparse,init,leading_slash,dotdot,roundtrip_metadata,
- apply_file_info_opts].
+ apply_file_info_opts,incompatible_options].
groups() ->
[].
@@ -574,6 +575,29 @@ extract_from_open_file(Config) when is_list(Config) ->
verify_ports(Config).
+%% Make sure incompatible options are rejected when opening archives with file
+%% descriptors.
+incompatible_options(Config) when is_list(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ Long = filename:join(DataDir, "no_fancy_stuff.tar"),
+
+ {ok, File} = file:open(Long, [read]),
+ Handle = {file, File},
+
+ {error, {Handle, {incompatible_option, compressed}}}
+ = erl_tar:open(Handle, [read, compressed]),
+ {error, {Handle, {incompatible_option, cooked}}}
+ = erl_tar:open(Handle, [read, cooked]),
+
+ {error, {Handle, {incompatible_option, compressed}}}
+ = erl_tar:extract(Handle, [compressed]),
+ {error, {Handle, {incompatible_option, cooked}}}
+ = erl_tar:extract(Handle, [cooked]),
+
+ ok = file:close(File),
+
+ verify_ports(Config).
+
%% Test that archives containing symlinks can be created and extracted.
symlinks(Config) when is_list(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
diff --git a/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt b/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt
index 6847953c23..fb4fec9fff 100644
--- a/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt
+++ b/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt
@@ -1,6 +1,6 @@
-# GraphemeBreakTest-11.0.0.txt
-# Date: 2018-03-18, 13:30:33 GMT
-# © 2018 Unicode®, Inc.
+# GraphemeBreakTest-12.1.0.txt
+# Date: 2019-03-10, 10:53:12 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -56,8 +56,6 @@
÷ 0020 × 0308 × 200D ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0020 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0020 × 0308 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0020 ÷ D800 ÷ # ÷ [0.2] SPACE (Other) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0020 × 0308 ÷ D800 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 000D ÷ 0020 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] SPACE (Other) ÷ [0.3]
÷ 000D ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 000D ÷ 000D ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -92,8 +90,6 @@
÷ 000D ÷ 0308 × 200D ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 000D ÷ 0378 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
÷ 000D ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000D ÷ D800 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 000A ÷ 0020 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] SPACE (Other) ÷ [0.3]
÷ 000A ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 000A ÷ 000D ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -128,8 +124,6 @@
÷ 000A ÷ 0308 × 200D ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 000A ÷ 0378 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
÷ 000A ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000A ÷ D800 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0001 ÷ 0020 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] SPACE (Other) ÷ [0.3]
÷ 0001 ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0001 ÷ 000D ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -164,8 +158,6 @@
÷ 0001 ÷ 0308 × 200D ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0001 ÷ 0378 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0001 ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0001 ÷ D800 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 034F ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 034F × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 034F ÷ 000D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -200,8 +192,6 @@
÷ 034F × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 034F ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 034F × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 034F ÷ D800 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 034F × 0308 ÷ D800 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 1F1E6 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1F1E6 × 0308 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1F1E6 ÷ 000D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -236,8 +226,6 @@
÷ 1F1E6 × 0308 × 200D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 1F1E6 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 1F1E6 × 0308 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1F1E6 ÷ D800 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ D800 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0600 × 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] SPACE (Other) ÷ [0.3]
÷ 0600 × 0308 ÷ 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0600 ÷ 000D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -272,8 +260,6 @@
÷ 0600 × 0308 × 200D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0600 × 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] <reserved-0378> (Other) ÷ [0.3]
÷ 0600 × 0308 ÷ 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0600 ÷ D800 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0600 × 0308 ÷ D800 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0903 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0903 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0903 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -308,8 +294,6 @@
÷ 0903 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0903 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0903 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0903 ÷ D800 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0903 × 0308 ÷ D800 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 1100 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1100 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1100 ÷ 000D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -344,8 +328,6 @@
÷ 1100 × 0308 × 200D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 1100 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 1100 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1100 ÷ D800 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 1100 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 1160 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1160 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1160 ÷ 000D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -380,8 +362,6 @@
÷ 1160 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 1160 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 1160 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1160 ÷ D800 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 1160 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 11A8 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 11A8 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 11A8 ÷ 000D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -416,8 +396,6 @@
÷ 11A8 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 11A8 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 11A8 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 11A8 ÷ D800 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 11A8 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ AC00 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC00 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC00 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -452,8 +430,6 @@
÷ AC00 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ AC00 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ AC00 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC00 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ AC00 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ AC01 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC01 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC01 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -488,8 +464,6 @@
÷ AC01 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ AC01 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ AC01 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC01 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ AC01 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 231A ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 231A × 0308 ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 231A ÷ 000D ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -524,8 +498,6 @@
÷ 231A × 0308 × 200D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 231A ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 231A × 0308 ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 231A ÷ D800 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 231A × 0308 ÷ D800 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0300 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0300 × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0300 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -560,8 +532,6 @@
÷ 0300 × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0300 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0300 × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0300 ÷ D800 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0300 × 0308 ÷ D800 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 200D ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 200D × 0308 ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 200D ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -596,8 +566,6 @@
÷ 200D × 0308 × 200D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 200D ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 200D × 0308 ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 200D ÷ D800 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 200D × 0308 ÷ D800 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0378 ÷ 0020 ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0378 × 0308 ÷ 0020 ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0378 ÷ 000D ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -632,44 +600,6 @@
÷ 0378 × 0308 × 200D ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0378 ÷ 0378 ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0378 × 0308 ÷ 0378 ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0378 ÷ D800 ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0378 × 0308 ÷ D800 ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ D800 ÷ 0020 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] SPACE (Other) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ D800 ÷ 000D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 000D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ D800 ÷ 000A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 000A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ D800 ÷ 0001 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0001 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ D800 ÷ 034F ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]
-÷ D800 ÷ 0308 × 034F ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]
-÷ D800 ÷ 1F1E6 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ D800 ÷ 0600 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0600 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ D800 ÷ 0903 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ D800 ÷ 0308 × 0903 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ D800 ÷ 1100 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ D800 ÷ 1160 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ D800 ÷ 11A8 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ D800 ÷ AC00 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ D800 ÷ AC01 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ D800 ÷ 231A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] WATCH (ExtPict) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 231A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ D800 ÷ 0300 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 0308 × 0300 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 200D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 0308 × 200D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 0378 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ D800 ÷ D800 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 000D × 000A ÷ 0061 ÷ 000A ÷ 0308 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [4.0] LATIN SMALL LETTER A (Other) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]
÷ 0061 × 0308 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]
÷ 0020 × 200D ÷ 0646 ÷ # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC LETTER NOON (Other) ÷ [0.3]
@@ -695,6 +625,6 @@
÷ 2701 × 200D × 2701 ÷ # ÷ [0.2] UPPER BLADE SCISSORS (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]
÷ 0061 × 200D ÷ 2701 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]
#
-# Lines: 672
+# Lines: 602
#
# EOF
diff --git a/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt b/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt
index 0e9e678a85..eb056990a0 100644
--- a/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt
+++ b/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt
@@ -1,6 +1,6 @@
-# LineBreakTest-11.0.0.txt
-# Date: 2018-05-20, 09:03:09 GMT
-# © 2018 Unicode®, Inc.
+# LineBreakTest-12.1.0.txt
+# Date: 2019-03-10, 10:53:14 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
diff --git a/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt b/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt
index 72a31bcdf1..54f43bdf4d 100644
--- a/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt
+++ b/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt
@@ -1,6 +1,6 @@
-# NormalizationTest-11.0.0.txt
-# Date: 2018-02-19, 18:33:08 GMT
-# © 2018 Unicode®, Inc.
+# NormalizationTest-12.1.0.txt
+# Date: 2019-04-01, 09:10:28 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -2149,6 +2149,7 @@
32FC;32FC;32FC;30F0;30F0; # (㋼; ㋼; ㋼; ヰ; ヰ; ) CIRCLED KATAKANA WI
32FD;32FD;32FD;30F1;30F1; # (㋽; ㋽; ㋽; ヱ; ヱ; ) CIRCLED KATAKANA WE
32FE;32FE;32FE;30F2;30F2; # (㋾; ㋾; ㋾; ヲ; ヲ; ) CIRCLED KATAKANA WO
+32FF;32FF;32FF;4EE4 548C;4EE4 548C; # (㋿; ㋿; ㋿; 令和; 令和; ) SQUARE ERA NAME REIWA
3300;3300;3300;30A2 30D1 30FC 30C8;30A2 30CF 309A 30FC 30C8; # (㌀; ㌀; ㌀; アパート; アハ◌゚ート; ) SQUARE APAATO
3301;3301;3301;30A2 30EB 30D5 30A1;30A2 30EB 30D5 30A1; # (㌁; ㌁; ㌁; アルファ; アルファ; ) SQUARE ARUHUA
3302;3302;3302;30A2 30F3 30DA 30A2;30A2 30F3 30D8 309A 30A2; # (㌂; ㌂; ㌂; アンペア; アンヘ◌゚ア; ) SQUARE ANPEA
@@ -16363,6 +16364,7 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
1F14F;1F14F;1F14F;0057 0043;0057 0043; # (🅏; 🅏; 🅏; WC; WC; ) SQUARED WC
1F16A;1F16A;1F16A;004D 0043;004D 0043; # (🅪; 🅪; 🅪; MC; MC; ) RAISED MC SIGN
1F16B;1F16B;1F16B;004D 0044;004D 0044; # (🅫; 🅫; 🅫; MD; MD; ) RAISED MD SIGN
+1F16C;1F16C;1F16C;004D 0052;004D 0052; # (🅬; 🅬; 🅬; MR; MR; ) RAISED MR SIGN
1F190;1F190;1F190;0044 004A;0044 004A; # (🆐; 🆐; 🆐; DJ; DJ; ) SQUARE DJ
1F200;1F200;1F200;307B 304B;307B 304B; # (🈀; 🈀; 🈀; ほか; ほか; ) SQUARE HIRAGANA HOKA
1F201;1F201;1F201;30B3 30B3;30B3 30B3; # (🈁; 🈁; 🈁; ココ; ココ; ) SQUARED KATAKANA KOKO
@@ -17685,6 +17687,8 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
0061 0EB8 0EC8 0EB8 0E48 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062; # (a◌ຸ◌່◌ຸ◌่b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B
0061 0EC8 0EB8 0E48 0EB9 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062; # (a◌່◌ຸ◌่◌ູb; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; ) LATIN SMALL LETTER A, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LAO VOWEL SIGN UU, LATIN SMALL LETTER B
0061 0EB9 0EC8 0EB8 0E48 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062; # (a◌ູ◌່◌ຸ◌่b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN UU, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0EBA 0062;0061 3099 094D 0EBA 05B0 0062;0061 3099 094D 0EBA 05B0 0062;0061 3099 094D 0EBA 05B0 0062;0061 3099 094D 0EBA 05B0 0062; # (a◌ְ◌्◌゙◌຺b; a◌゙◌्◌຺◌ְb; a◌゙◌्◌຺◌ְb; a◌゙◌्◌຺◌ְb; a◌゙◌्◌຺◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LAO SIGN PALI VIRAMA, LATIN SMALL LETTER B
+0061 0EBA 05B0 094D 3099 0062;0061 3099 0EBA 094D 05B0 0062;0061 3099 0EBA 094D 05B0 0062;0061 3099 0EBA 094D 05B0 0062;0061 3099 0EBA 094D 05B0 0062; # (a◌຺◌ְ◌्◌゙b; a◌゙◌຺◌्◌ְb; a◌゙◌຺◌्◌ְb; a◌゙◌຺◌्◌ְb; a◌゙◌຺◌्◌ְb; ) LATIN SMALL LETTER A, LAO SIGN PALI VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 0F71 0EC8 0EB8 0EC8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌ཱ◌່◌ຸ◌່b; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI EK, LATIN SMALL LETTER B
0061 0EC8 0F71 0EC8 0EB8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌່◌ཱ◌່◌ຸb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI EK, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B
0061 0F71 0EC8 0EB8 0EC9 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062; # (a◌ཱ◌່◌ຸ◌້b; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI THO, LATIN SMALL LETTER B
@@ -18453,6 +18457,8 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
0061 11839 05B0 094D 3099 0062;0061 3099 11839 094D 05B0 0062;0061 3099 11839 094D 05B0 0062;0061 3099 11839 094D 05B0 0062;0061 3099 11839 094D 05B0 0062; # (a◌𑠹◌ְ◌्◌゙b; a◌゙◌𑠹◌्◌ְb; a◌゙◌𑠹◌्◌ְb; a◌゙◌𑠹◌्◌ְb; a◌゙◌𑠹◌्◌ְb; ) LATIN SMALL LETTER A, DOGRA SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 3099 093C 0334 1183A 0062;0061 0334 093C 1183A 3099 0062;0061 0334 093C 1183A 3099 0062;0061 0334 093C 1183A 3099 0062;0061 0334 093C 1183A 3099 0062; # (a◌゙◌़◌̴◌𑠺b; a◌̴◌़◌𑠺◌゙b; a◌̴◌़◌𑠺◌゙b; a◌̴◌़◌𑠺◌゙b; a◌̴◌़◌𑠺◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, DOGRA SIGN NUKTA, LATIN SMALL LETTER B
0061 1183A 3099 093C 0334 0062;0061 0334 1183A 093C 3099 0062;0061 0334 1183A 093C 3099 0062;0061 0334 1183A 093C 3099 0062;0061 0334 1183A 093C 3099 0062; # (a◌𑠺◌゙◌़◌̴b; a◌̴◌𑠺◌़◌゙b; a◌̴◌𑠺◌़◌゙b; a◌̴◌𑠺◌़◌゙b; a◌̴◌𑠺◌़◌゙b; ) LATIN SMALL LETTER A, DOGRA SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 119E0 0062;0061 3099 094D 119E0 05B0 0062;0061 3099 094D 119E0 05B0 0062;0061 3099 094D 119E0 05B0 0062;0061 3099 094D 119E0 05B0 0062; # (a◌ְ◌्◌゙◌𑧠b; a◌゙◌्◌𑧠◌ְb; a◌゙◌्◌𑧠◌ְb; a◌゙◌्◌𑧠◌ְb; a◌゙◌्◌𑧠◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, NANDINAGARI SIGN VIRAMA, LATIN SMALL LETTER B
+0061 119E0 05B0 094D 3099 0062;0061 3099 119E0 094D 05B0 0062;0061 3099 119E0 094D 05B0 0062;0061 3099 119E0 094D 05B0 0062;0061 3099 119E0 094D 05B0 0062; # (a◌𑧠◌ְ◌्◌゙b; a◌゙◌𑧠◌्◌ְb; a◌゙◌𑧠◌्◌ְb; a◌゙◌𑧠◌्◌ְb; a◌゙◌𑧠◌्◌ְb; ) LATIN SMALL LETTER A, NANDINAGARI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 05B0 094D 3099 11A34 0062;0061 3099 094D 11A34 05B0 0062;0061 3099 094D 11A34 05B0 0062;0061 3099 094D 11A34 05B0 0062;0061 3099 094D 11A34 05B0 0062; # (a◌ְ◌्◌゙◌𑨴b; a◌゙◌्◌𑨴◌ְb; a◌゙◌्◌𑨴◌ְb; a◌゙◌्◌𑨴◌ְb; a◌゙◌्◌𑨴◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, ZANABAZAR SQUARE SIGN VIRAMA, LATIN SMALL LETTER B
0061 11A34 05B0 094D 3099 0062;0061 3099 11A34 094D 05B0 0062;0061 3099 11A34 094D 05B0 0062;0061 3099 11A34 094D 05B0 0062;0061 3099 11A34 094D 05B0 0062; # (a◌𑨴◌ְ◌्◌゙b; a◌゙◌𑨴◌्◌ְb; a◌゙◌𑨴◌्◌ְb; a◌゙◌𑨴◌्◌ְb; a◌゙◌𑨴◌्◌ְb; ) LATIN SMALL LETTER A, ZANABAZAR SQUARE SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 05B0 094D 3099 11A47 0062;0061 3099 094D 11A47 05B0 0062;0061 3099 094D 11A47 05B0 0062;0061 3099 094D 11A47 05B0 0062;0061 3099 094D 11A47 05B0 0062; # (a◌ְ◌्◌゙◌𑩇b; a◌゙◌्◌𑩇◌ְb; a◌゙◌्◌𑩇◌ְb; a◌゙◌्◌𑩇◌ְb; a◌゙◌्◌𑩇◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, ZANABAZAR SQUARE SUBJOINER, LATIN SMALL LETTER B
@@ -18637,6 +18643,28 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
0061 1E029 0315 0300 05AE 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062; # (a◌𞀩◌̕◌̀◌֮b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GLAGOLITIC LETTER IOTATED BIG YUS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
0061 0315 0300 05AE 1E02A 0062;00E0 05AE 1E02A 0315 0062;0061 05AE 0300 1E02A 0315 0062;00E0 05AE 1E02A 0315 0062;0061 05AE 0300 1E02A 0315 0062; # (a◌̕◌̀◌֮◌𞀪b; à◌֮◌𞀪◌̕b; a◌֮◌̀◌𞀪◌̕b; à◌֮◌𞀪◌̕b; a◌֮◌̀◌𞀪◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GLAGOLITIC LETTER FITA, LATIN SMALL LETTER B
0061 1E02A 0315 0300 05AE 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062; # (a◌𞀪◌̕◌̀◌֮b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GLAGOLITIC LETTER FITA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E130 0062;00E0 05AE 1E130 0315 0062;0061 05AE 0300 1E130 0315 0062;00E0 05AE 1E130 0315 0062;0061 05AE 0300 1E130 0315 0062; # (a◌̕◌̀◌֮◌𞄰b; à◌֮◌𞄰◌̕b; a◌֮◌̀◌𞄰◌̕b; à◌֮◌𞄰◌̕b; a◌֮◌̀◌𞄰◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-B, LATIN SMALL LETTER B
+0061 1E130 0315 0300 05AE 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062; # (a◌𞄰◌̕◌̀◌֮b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-B, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E131 0062;00E0 05AE 1E131 0315 0062;0061 05AE 0300 1E131 0315 0062;00E0 05AE 1E131 0315 0062;0061 05AE 0300 1E131 0315 0062; # (a◌̕◌̀◌֮◌𞄱b; à◌֮◌𞄱◌̕b; a◌֮◌̀◌𞄱◌̕b; à◌֮◌𞄱◌̕b; a◌֮◌̀◌𞄱◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-M, LATIN SMALL LETTER B
+0061 1E131 0315 0300 05AE 0062;0061 05AE 1E131 0300 0315 0062;0061 05AE 1E131 0300 0315 0062;0061 05AE 1E131 0300 0315 0062;0061 05AE 1E131 0300 0315 0062; # (a◌𞄱◌̕◌̀◌֮b; a◌֮◌𞄱◌̀◌̕b; a◌֮◌𞄱◌̀◌̕b; a◌֮◌𞄱◌̀◌̕b; a◌֮◌𞄱◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-M, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E132 0062;00E0 05AE 1E132 0315 0062;0061 05AE 0300 1E132 0315 0062;00E0 05AE 1E132 0315 0062;0061 05AE 0300 1E132 0315 0062; # (a◌̕◌̀◌֮◌𞄲b; à◌֮◌𞄲◌̕b; a◌֮◌̀◌𞄲◌̕b; à◌֮◌𞄲◌̕b; a◌֮◌̀◌𞄲◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-J, LATIN SMALL LETTER B
+0061 1E132 0315 0300 05AE 0062;0061 05AE 1E132 0300 0315 0062;0061 05AE 1E132 0300 0315 0062;0061 05AE 1E132 0300 0315 0062;0061 05AE 1E132 0300 0315 0062; # (a◌𞄲◌̕◌̀◌֮b; a◌֮◌𞄲◌̀◌̕b; a◌֮◌𞄲◌̀◌̕b; a◌֮◌𞄲◌̀◌̕b; a◌֮◌𞄲◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-J, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E133 0062;00E0 05AE 1E133 0315 0062;0061 05AE 0300 1E133 0315 0062;00E0 05AE 1E133 0315 0062;0061 05AE 0300 1E133 0315 0062; # (a◌̕◌̀◌֮◌𞄳b; à◌֮◌𞄳◌̕b; a◌֮◌̀◌𞄳◌̕b; à◌֮◌𞄳◌̕b; a◌֮◌̀◌𞄳◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-V, LATIN SMALL LETTER B
+0061 1E133 0315 0300 05AE 0062;0061 05AE 1E133 0300 0315 0062;0061 05AE 1E133 0300 0315 0062;0061 05AE 1E133 0300 0315 0062;0061 05AE 1E133 0300 0315 0062; # (a◌𞄳◌̕◌̀◌֮b; a◌֮◌𞄳◌̀◌̕b; a◌֮◌𞄳◌̀◌̕b; a◌֮◌𞄳◌̀◌̕b; a◌֮◌𞄳◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-V, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E134 0062;00E0 05AE 1E134 0315 0062;0061 05AE 0300 1E134 0315 0062;00E0 05AE 1E134 0315 0062;0061 05AE 0300 1E134 0315 0062; # (a◌̕◌̀◌֮◌𞄴b; à◌֮◌𞄴◌̕b; a◌֮◌̀◌𞄴◌̕b; à◌֮◌𞄴◌̕b; a◌֮◌̀◌𞄴◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-S, LATIN SMALL LETTER B
+0061 1E134 0315 0300 05AE 0062;0061 05AE 1E134 0300 0315 0062;0061 05AE 1E134 0300 0315 0062;0061 05AE 1E134 0300 0315 0062;0061 05AE 1E134 0300 0315 0062; # (a◌𞄴◌̕◌̀◌֮b; a◌֮◌𞄴◌̀◌̕b; a◌֮◌𞄴◌̀◌̕b; a◌֮◌𞄴◌̀◌̕b; a◌֮◌𞄴◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-S, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E135 0062;00E0 05AE 1E135 0315 0062;0061 05AE 0300 1E135 0315 0062;00E0 05AE 1E135 0315 0062;0061 05AE 0300 1E135 0315 0062; # (a◌̕◌̀◌֮◌𞄵b; à◌֮◌𞄵◌̕b; a◌֮◌̀◌𞄵◌̕b; à◌֮◌𞄵◌̕b; a◌֮◌̀◌𞄵◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-G, LATIN SMALL LETTER B
+0061 1E135 0315 0300 05AE 0062;0061 05AE 1E135 0300 0315 0062;0061 05AE 1E135 0300 0315 0062;0061 05AE 1E135 0300 0315 0062;0061 05AE 1E135 0300 0315 0062; # (a◌𞄵◌̕◌̀◌֮b; a◌֮◌𞄵◌̀◌̕b; a◌֮◌𞄵◌̀◌̕b; a◌֮◌𞄵◌̀◌̕b; a◌֮◌𞄵◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-G, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E136 0062;00E0 05AE 1E136 0315 0062;0061 05AE 0300 1E136 0315 0062;00E0 05AE 1E136 0315 0062;0061 05AE 0300 1E136 0315 0062; # (a◌̕◌̀◌֮◌𞄶b; à◌֮◌𞄶◌̕b; a◌֮◌̀◌𞄶◌̕b; à◌֮◌𞄶◌̕b; a◌֮◌̀◌𞄶◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-D, LATIN SMALL LETTER B
+0061 1E136 0315 0300 05AE 0062;0061 05AE 1E136 0300 0315 0062;0061 05AE 1E136 0300 0315 0062;0061 05AE 1E136 0300 0315 0062;0061 05AE 1E136 0300 0315 0062; # (a◌𞄶◌̕◌̀◌֮b; a◌֮◌𞄶◌̀◌̕b; a◌֮◌𞄶◌̀◌̕b; a◌֮◌𞄶◌̀◌̕b; a◌֮◌𞄶◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-D, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2EC 0062;00E0 05AE 1E2EC 0315 0062;0061 05AE 0300 1E2EC 0315 0062;00E0 05AE 1E2EC 0315 0062;0061 05AE 0300 1E2EC 0315 0062; # (a◌̕◌̀◌֮◌𞋬b; à◌֮◌𞋬◌̕b; a◌֮◌̀◌𞋬◌̕b; à◌֮◌𞋬◌̕b; a◌֮◌̀◌𞋬◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE TUP, LATIN SMALL LETTER B
+0061 1E2EC 0315 0300 05AE 0062;0061 05AE 1E2EC 0300 0315 0062;0061 05AE 1E2EC 0300 0315 0062;0061 05AE 1E2EC 0300 0315 0062;0061 05AE 1E2EC 0300 0315 0062; # (a◌𞋬◌̕◌̀◌֮b; a◌֮◌𞋬◌̀◌̕b; a◌֮◌𞋬◌̀◌̕b; a◌֮◌𞋬◌̀◌̕b; a◌֮◌𞋬◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE TUP, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2ED 0062;00E0 05AE 1E2ED 0315 0062;0061 05AE 0300 1E2ED 0315 0062;00E0 05AE 1E2ED 0315 0062;0061 05AE 0300 1E2ED 0315 0062; # (a◌̕◌̀◌֮◌𞋭b; à◌֮◌𞋭◌̕b; a◌֮◌̀◌𞋭◌̕b; à◌֮◌𞋭◌̕b; a◌֮◌̀◌𞋭◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE TUPNI, LATIN SMALL LETTER B
+0061 1E2ED 0315 0300 05AE 0062;0061 05AE 1E2ED 0300 0315 0062;0061 05AE 1E2ED 0300 0315 0062;0061 05AE 1E2ED 0300 0315 0062;0061 05AE 1E2ED 0300 0315 0062; # (a◌𞋭◌̕◌̀◌֮b; a◌֮◌𞋭◌̀◌̕b; a◌֮◌𞋭◌̀◌̕b; a◌֮◌𞋭◌̀◌̕b; a◌֮◌𞋭◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE TUPNI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2EE 0062;00E0 05AE 1E2EE 0315 0062;0061 05AE 0300 1E2EE 0315 0062;00E0 05AE 1E2EE 0315 0062;0061 05AE 0300 1E2EE 0315 0062; # (a◌̕◌̀◌֮◌𞋮b; à◌֮◌𞋮◌̕b; a◌֮◌̀◌𞋮◌̕b; à◌֮◌𞋮◌̕b; a◌֮◌̀◌𞋮◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE KOI, LATIN SMALL LETTER B
+0061 1E2EE 0315 0300 05AE 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062; # (a◌𞋮◌̕◌̀◌֮b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE KOI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2EF 0062;00E0 05AE 1E2EF 0315 0062;0061 05AE 0300 1E2EF 0315 0062;00E0 05AE 1E2EF 0315 0062;0061 05AE 0300 1E2EF 0315 0062; # (a◌̕◌̀◌֮◌𞋯b; à◌֮◌𞋯◌̕b; a◌֮◌̀◌𞋯◌̕b; à◌֮◌𞋯◌̕b; a◌֮◌̀◌𞋯◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE KOINI, LATIN SMALL LETTER B
+0061 1E2EF 0315 0300 05AE 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062; # (a◌𞋯◌̕◌̀◌֮b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE KOINI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
0061 059A 0316 302A 1E8D0 0062;0061 302A 0316 1E8D0 059A 0062;0061 302A 0316 1E8D0 059A 0062;0061 302A 0316 1E8D0 059A 0062;0061 302A 0316 1E8D0 059A 0062; # (a◌֚◌̖◌〪◌𞣐b; a◌〪◌̖◌𞣐◌֚b; a◌〪◌̖◌𞣐◌֚b; a◌〪◌̖◌𞣐◌֚b; a◌〪◌̖◌𞣐◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MENDE KIKAKUI COMBINING NUMBER TEENS, LATIN SMALL LETTER B
0061 1E8D0 059A 0316 302A 0062;0061 302A 1E8D0 0316 059A 0062;0061 302A 1E8D0 0316 059A 0062;0061 302A 1E8D0 0316 059A 0062;0061 302A 1E8D0 0316 059A 0062; # (a◌𞣐◌֚◌̖◌〪b; a◌〪◌𞣐◌̖◌֚b; a◌〪◌𞣐◌̖◌֚b; a◌〪◌𞣐◌̖◌֚b; a◌〪◌𞣐◌̖◌֚b; ) LATIN SMALL LETTER A, MENDE KIKAKUI COMBINING NUMBER TEENS, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
0061 059A 0316 302A 1E8D1 0062;0061 302A 0316 1E8D1 059A 0062;0061 302A 0316 1E8D1 059A 0062;0061 302A 0316 1E8D1 059A 0062;0061 302A 0316 1E8D1 059A 0062; # (a◌֚◌̖◌〪◌𞣑b; a◌〪◌̖◌𞣑◌֚b; a◌〪◌̖◌𞣑◌֚b; a◌〪◌̖◌𞣑◌֚b; a◌〪◌̖◌𞣑◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MENDE KIKAKUI COMBINING NUMBER TENS, LATIN SMALL LETTER B
diff --git a/lib/stdlib/test/uri_string_SUITE.erl b/lib/stdlib/test/uri_string_SUITE.erl
index eb59ea9b0c..96e4a9d41b 100644
--- a/lib/stdlib/test/uri_string_SUITE.erl
+++ b/lib/stdlib/test/uri_string_SUITE.erl
@@ -51,7 +51,9 @@
compose_query/1, compose_query_latin1/1, compose_query_negative/1,
dissect_query/1, dissect_query_negative/1,
interop_query_latin1/1, interop_query_utf8/1,
- regression_parse/1, regression_recompose/1, regression_normalize/1
+ regression_parse/1, regression_recompose/1, regression_normalize/1,
+ recompose_host_relative_path/1,
+ recompose_host_absolute_path/1
]).
@@ -144,7 +146,9 @@ all() ->
interop_query_utf8,
regression_parse,
regression_recompose,
- regression_normalize
+ regression_normalize,
+ recompose_host_relative_path,
+ recompose_host_absolute_path
].
groups() ->
@@ -1047,14 +1051,20 @@ normalize_map(_Config) ->
normalize_return_map(_Config) ->
#{scheme := "http",path := "/a/g",host := "localhost-örebro"} =
- uri_string:normalize("http://localhos%74-%c3%b6rebro:80/a/b/c/./../../g",
- [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ "http://localhos%74-%c3%b6rebro:80/a/b/c/./../../g",
+ [return_map])),
#{scheme := <<"http">>,path := <<"/a/g">>, host := <<"localhost-örebro"/utf8>>} =
- uri_string:normalize(<<"http://localhos%74-%c3%b6rebro:80/a/b/c/./../../g">>,
- [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"http://localhos%74-%c3%b6rebro:80/a/b/c/./../../g">>,
+ [return_map])),
#{scheme := <<"https">>,path := <<"/">>, host := <<"localhost">>} =
- uri_string:normalize(#{scheme => <<"https">>,port => 443,path => <<>>,
- host => <<"localhost">>}, [return_map]).
+ uri_string:percent_decode(
+ uri_string:normalize(
+ #{scheme => <<"https">>,port => 443,path => <<>>,
+ host => <<"localhost">>}, [return_map])).
normalize_negative(_Config) ->
{error,invalid_uri,":"} =
@@ -1065,64 +1075,103 @@ normalize_negative(_Config) ->
uri_string:normalize("http://[192.168.0.1]", [return_map]),
{error,invalid_uri,":"} =
uri_string:normalize(<<"http://[192.168.0.1]">>, [return_map]),
- {error,invalid_utf8,<<0,0,0,246>>} = uri_string:normalize("//%00%00%00%F6").
+ {error,invalid_utf8,<<47,47,0,0,0,246>>} =
+ uri_string:percent_decode(uri_string:normalize("//%00%00%00%F6")).
normalize_binary_pct_encoded_userinfo(_Config) ->
#{scheme := <<"user">>, path := <<"合@気道"/utf8>>} =
- uri_string:normalize(<<"user:%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"user:%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map])),
#{path := <<"合気道@"/utf8>>} =
- uri_string:normalize(<<"%E5%90%88%E6%B0%97%E9%81%93@">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"%E5%90%88%E6%B0%97%E9%81%93@">>, [return_map])),
#{path := <<"/合気道@"/utf8>>} =
- uri_string:normalize(<<"/%E5%90%88%E6%B0%97%E9%81%93@">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"/%E5%90%88%E6%B0%97%E9%81%93@">>, [return_map])),
#{path := <<"合@気道"/utf8>>} =
- uri_string:normalize(<<"%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map])),
#{userinfo := <<"合"/utf8>>, host := <<"気道"/utf8>>} =
- uri_string:normalize(<<"//%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"//%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map])),
#{userinfo := <<"合:気"/utf8>>, host := <<"道"/utf8>>} =
- uri_string:normalize(<<"//%E5%90%88:%E6%B0%97@%E9%81%93">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"//%E5%90%88:%E6%B0%97@%E9%81%93">>, [return_map])),
#{scheme := <<"foo">>, path := <<"/合気道@"/utf8>>} =
- uri_string:normalize(<<"foo:/%E5%90%88%E6%B0%97%E9%81%93@">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"foo:/%E5%90%88%E6%B0%97%E9%81%93@">>, [return_map])),
#{scheme := <<"foo">>, userinfo := <<"合"/utf8>>, host := <<"気道"/utf8>>} =
- uri_string:normalize(<<"foo://%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"foo://%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map])),
#{scheme := <<"foo">>, userinfo := <<"合:気"/utf8>>, host := <<"道"/utf8>>} =
- uri_string:normalize(<<"foo://%E5%90%88:%E6%B0%97@%E9%81%93">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"foo://%E5%90%88:%E6%B0%97@%E9%81%93">>, [return_map])),
{error,invalid_uri,"@"} =
- uri_string:normalize(<<"//%E5%90%88@%E6%B0%97%E9%81%93@">>, [return_map]),
+ uri_string:normalize(
+ <<"//%E5%90%88@%E6%B0%97%E9%81%93@">>, [return_map]),
{error,invalid_uri,":"} =
- uri_string:normalize(<<"foo://%E5%90%88@%E6%B0%97%E9%81%93@">>, [return_map]).
+ uri_string:normalize(
+ <<"foo://%E5%90%88@%E6%B0%97%E9%81%93@">>, [return_map]).
normalize_binary_pct_encoded_query(_Config) ->
#{scheme := <<"foo">>, host := <<"example.com">>, path := <<"/">>,
query := <<"name=合気道"/utf8>>} =
- uri_string:normalize(<<"foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93">>,
+ [return_map])),
#{host := <<"example.com">>, path := <<"/">>, query := <<"name=合気道"/utf8>>} =
- uri_string:normalize(<<"//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93">>, [return_map]).
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93">>, [return_map])).
normalize_binary_pct_encoded_fragment(_Config) ->
#{scheme := <<"foo">>, host := <<"example.com">>, fragment := <<"合気道"/utf8>>} =
- uri_string:normalize(<<"foo://example.com#%E5%90%88%E6%B0%97%E9%81%93">>, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"foo://example.com#%E5%90%88%E6%B0%97%E9%81%93">>, [return_map])),
#{host := <<"example.com">>, path := <<"/">>, fragment := <<"合気道"/utf8>>} =
- uri_string:normalize(<<"//example.com/#%E5%90%88%E6%B0%97%E9%81%93">>, [return_map]).
+ uri_string:percent_decode(
+ uri_string:normalize(
+ <<"//example.com/#%E5%90%88%E6%B0%97%E9%81%93">>, [return_map])).
normalize_pct_encoded_userinfo(_Config) ->
#{scheme := "user", path := "合@気道"} =
- uri_string:normalize("user:%E5%90%88@%E6%B0%97%E9%81%93", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize("user:%E5%90%88@%E6%B0%97%E9%81%93", [return_map])),
#{path := "合気道@"} =
- uri_string:normalize("%E5%90%88%E6%B0%97%E9%81%93@", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize("%E5%90%88%E6%B0%97%E9%81%93@", [return_map])),
#{path := "/合気道@"} =
- uri_string:normalize("/%E5%90%88%E6%B0%97%E9%81%93@", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize("/%E5%90%88%E6%B0%97%E9%81%93@", [return_map])),
#{path := "合@気道"} =
- uri_string:normalize("%E5%90%88@%E6%B0%97%E9%81%93", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize("%E5%90%88@%E6%B0%97%E9%81%93", [return_map])),
#{userinfo := "合", host := "気道"} =
- uri_string:normalize("//%E5%90%88@%E6%B0%97%E9%81%93", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize("//%E5%90%88@%E6%B0%97%E9%81%93", [return_map])),
#{userinfo := "合:気", host := "道"} =
- uri_string:normalize("//%E5%90%88:%E6%B0%97@%E9%81%93", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize("//%E5%90%88:%E6%B0%97@%E9%81%93", [return_map])),
#{scheme := "foo", path := "/合気道@"} =
- uri_string:normalize("foo:/%E5%90%88%E6%B0%97%E9%81%93@", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize("foo:/%E5%90%88%E6%B0%97%E9%81%93@", [return_map])),
#{scheme := "foo", userinfo := "合", host := "気道"} =
- uri_string:normalize("foo://%E5%90%88@%E6%B0%97%E9%81%93", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize("foo://%E5%90%88@%E6%B0%97%E9%81%93", [return_map])),
#{scheme := "foo", userinfo := "合:気", host := "道"} =
- uri_string:normalize("foo://%E5%90%88:%E6%B0%97@%E9%81%93", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize("foo://%E5%90%88:%E6%B0%97@%E9%81%93", [return_map])),
{error,invalid_uri,"@"} =
uri_string:normalize("//%E5%90%88@%E6%B0%97%E9%81%93@", [return_map]),
{error,invalid_uri,":"} =
@@ -1131,25 +1180,37 @@ normalize_pct_encoded_userinfo(_Config) ->
normalize_pct_encoded_query(_Config) ->
#{scheme := "foo", host := "example.com", path := "/",
query := "name=合気道"} =
- uri_string:normalize("foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ "foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93", [return_map])),
#{host := "example.com", path := "/", query := "name=合気道"} =
- uri_string:normalize("//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93", [return_map]).
+ uri_string:percent_decode(
+ uri_string:normalize(
+ "//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93", [return_map])).
normalize_pct_encoded_fragment(_Config) ->
#{scheme := "foo", host := "example.com", fragment := "合気道"} =
- uri_string:normalize("foo://example.com#%E5%90%88%E6%B0%97%E9%81%93", [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(
+ "foo://example.com#%E5%90%88%E6%B0%97%E9%81%93", [return_map])),
#{host := "example.com", path := "/", fragment := "合気道"} =
- uri_string:normalize("//example.com/#%E5%90%88%E6%B0%97%E9%81%93", [return_map]).
+ uri_string:percent_decode(
+ uri_string:normalize(
+ "//example.com/#%E5%90%88%E6%B0%97%E9%81%93", [return_map])).
normalize_pct_encoded_negative(_Config) ->
- {error,invalid_utf8,<<0,0,0,246>>} =
- uri_string:normalize(#{host => "%00%00%00%F6",path => []}, [return_map]),
- {error,invalid_utf8,<<0,0,0,246>>} =
- uri_string:normalize(#{host => "%00%00%00%F6",path => []}, []),
- {error,invalid_utf8,<<0,0,0,246>>} =
- uri_string:normalize("//%00%00%00%F6", [return_map]),
- {error,invalid_utf8,<<0,0,0,246>>} =
- uri_string:normalize("//%00%00%00%F6", []).
+ {error,{invalid,{host,{invalid_utf8,<<0,0,0,246>>}}}} =
+ uri_string:percent_decode(
+ uri_string:normalize(#{host => "%00%00%00%F6",path => []}, [return_map])),
+ {error,invalid_utf8,<<47,47,0,0,0,246>>} =
+ uri_string:percent_decode(
+ uri_string:normalize(#{host => "%00%00%00%F6",path => []}, [])),
+ {error,{invalid,{host,{invalid_utf8,<<0,0,0,246>>}}}} =
+ uri_string:percent_decode(
+ uri_string:normalize("//%00%00%00%F6", [return_map])),
+ {error,invalid_utf8,<<47,47,0,0,0,246>>} =
+ uri_string:percent_decode(
+ uri_string:normalize("//%00%00%00%F6", [])).
interop_query_utf8(_Config) ->
Q = uri_string:compose_query([{"foo bar","1"}, {"合", "2"}]),
@@ -1214,8 +1275,8 @@ regression_normalize(_Config) ->
"foo://%C3%B6" =
uri_string:normalize("FOo://%C3%B6"),
#{host := "ö",path := [],scheme := "foo"} =
- uri_string:normalize("FOo://%C3%B6", [return_map]),
-
+ uri_string:percent_decode(
+ uri_string:normalize("FOo://%C3%B6", [return_map])),
"foo://bar" =
uri_string:normalize(#{host => "Bar",path => [],scheme => "FOo"}),
@@ -1240,9 +1301,34 @@ regression_normalize(_Config) ->
"foo://%C3%B6" =
uri_string:normalize(#{host => "%C3%B6",path => [],scheme => "FOo"}),
#{host := "ö",path := [],scheme := "foo"} =
- uri_string:normalize(#{host => "%C3%B6",path => [],scheme => "FOo"}, [return_map]),
+ uri_string:percent_decode(
+ uri_string:normalize(#{host => "%C3%B6",path => [],scheme => "FOo"},
+ [return_map])),
"foo://%C3%B6" =
uri_string:normalize(#{host => "ö",path => [],scheme => "FOo"}),
#{host := "ö",path := [],scheme := "foo"} =
uri_string:normalize(#{host => "ö",path => [],scheme => "FOo"}, [return_map]).
+
+recompose_host_relative_path(_Config) ->
+ "//example.com/.foo" =
+ uri_string:recompose(#{host => "example.com", path => ".foo"}),
+ <<"//example.com/foo">> =
+ uri_string:recompose(#{host => <<"example.com">>, path => <<"foo">>}),
+ ok.
+
+recompose_host_absolute_path(_Config) ->
+ "//example.com/foo" =
+ uri_string:recompose(#{host => "example.com",
+ path => ["/", "foo"]}),
+ "//example.com/foo" =
+ uri_string:recompose(#{host => <<"example.com">>,
+ path => [<<"/">>,<<"foo">>]}),
+ "//example.com/foo" =
+ uri_string:recompose(#{host => "example.com",
+ path => ["/f", "oo"]}),
+ "//example.com/foo" =
+ uri_string:recompose(#{host => <<"example.com">>,
+ path => [<<"/f">>,<<"oo">>]}),
+ ok.
+
diff --git a/lib/stdlib/test/win32reg_SUITE.erl b/lib/stdlib/test/win32reg_SUITE.erl
index 5e44e16ddc..f7d3d8da97 100644
--- a/lib/stdlib/test/win32reg_SUITE.erl
+++ b/lib/stdlib/test/win32reg_SUITE.erl
@@ -59,23 +59,27 @@ long(Config) when is_list(Config) ->
{ok,Read} = win32reg:open([read]),
ok = win32reg:change_key(Read, "\\hklm"),
- ok = win32reg:change_key(Read, LongKey),
- {ok,ErlangKey} = win32reg:current_key(Read),
- io:format("Erlang key: ~s~n", [ErlangKey]),
- ok = win32reg:close(Read),
-
- {ok,Reg} = win32reg:open([read, write]),
- %% Write a long value and read it back.
- TestKey = "test_key",
- LongValue = lists:concat(["This is a long value generated by the test case ",?MODULE,":long/1. "|lists:duplicate(128, "a")]),
- ok = win32reg:set_value(Reg, TestKey, LongValue),
- {ok,LongValue} = win32reg:value(Reg, TestKey),
-
- io:format("Where ~p Key ~s Value ~s ~n", [win32reg:current_key(Reg), TestKey, LongValue]),
- %% Done.
-
- ok = win32reg:close(Reg),
- ok.
+ case os:getenv("WSLENV") of
+ false ->
+ ok = win32reg:change_key(Read, LongKey),
+ {ok,ErlangKey} = win32reg:current_key(Read),
+ io:format("Erlang key: ~s~n", [ErlangKey]),
+ ok = win32reg:close(Read),
+
+ {ok,Reg} = win32reg:open([read, write]),
+ %% Write a long value and read it back.
+ TestKey = "test_key",
+ LongValue = lists:concat(["This is a long value generated by the test case ",?MODULE,":long/1. "|lists:duplicate(128, "a")]),
+ ok = win32reg:set_value(Reg, TestKey, LongValue),
+ {ok,LongValue} = win32reg:value(Reg, TestKey),
+
+ io:format("Where ~p Key ~s Value ~s ~n", [win32reg:current_key(Reg), TestKey, LongValue]),
+ %% Done.
+ ok = win32reg:close(Reg);
+ _ ->
+ %% We have installed erlang when testing on win10 and newer
+ ok
+ end.
evil_write(Config) when is_list(Config) ->
Key = "Software\\Ericsson\\Erlang",
diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl
index 081bffa7cb..c1be2786da 100644
--- a/lib/stdlib/test/zip_SUITE.erl
+++ b/lib/stdlib/test/zip_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
openzip_api/1, zip_api/1, open_leak/1, unzip_jar/1,
unzip_traversal_exploit/1,
compress_control/1,
- foldl/1,fd_leak/1]).
+ foldl/1,fd_leak/1,unicode/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
@@ -40,7 +40,7 @@ all() ->
unzip_to_binary, zip_to_binary, unzip_options,
zip_options, list_dir_options, aliases, openzip_api,
zip_api, open_leak, unzip_jar, compress_control, foldl,
- unzip_traversal_exploit,fd_leak].
+ unzip_traversal_exploit,fd_leak,unicode].
groups() ->
[].
@@ -105,49 +105,32 @@ borderline_test(Size, TempDir) ->
%% Verify that Unix zip can read it. (if we have a unix zip that is!)
- unzip_list(Archive, Name),
+ zipinfo_match(Archive, Name),
ok.
-unzip_list(Archive, Name) ->
- case unix_unzip_exists() of
- true ->
- unzip_list1(Archive, Name);
+zipinfo_match(Archive, Name) ->
+ case check_zipinfo_exists() of
+ true ->
+ Encoding = file:native_name_encoding(),
+ Expect = unicode:characters_to_binary(Name ++ "\n",
+ Encoding, Encoding),
+ cmd_expect("zipinfo -1 " ++ Archive, Expect);
_ ->
ok
end.
-%% Used to do os:find_executable() to check if unzip exists, but on
-%% some hosts that would give an unzip program which did not take the
-%% "-Z" option.
-%% Here we check that "unzip -Z" (which should display usage) and
-%% check that it exists with status 0.
-unix_unzip_exists() ->
- case os:type() of
- {unix,_} ->
- Port = open_port({spawn,"unzip -Z > /dev/null"}, [exit_status]),
- receive
- {Port,{exit_status,0}} ->
- true;
- {Port,{exit_status,_Fail}} ->
- false
- end;
- _ ->
- false
- end.
-
-unzip_list1(Archive, Name) ->
- Expect = Name ++ "\n",
- cmd_expect("unzip -Z -1 " ++ Archive, Expect).
+check_zipinfo_exists() ->
+ is_list(os:find_executable("zipinfo")).
cmd_expect(Cmd, Expect) ->
- Port = open_port({spawn, make_cmd(Cmd)}, [stream, in, eof]),
- get_data(Port, Expect).
+ Port = open_port({spawn, make_cmd(Cmd)}, [stream, in, binary, eof]),
+ get_data(Port, Expect, <<>>).
-get_data(Port, Expect) ->
+get_data(Port, Expect, Acc) ->
receive
{Port, {data, Bytes}} ->
- get_data(Port, match_output(Bytes, Expect, Port));
+ get_data(Port, Expect, <<Acc/binary, Bytes/binary>>);
{Port, eof} ->
Port ! {self(), close},
receive
@@ -160,21 +143,17 @@ get_data(Port, Expect) ->
after 1 -> % force context switch
ok
end,
- match_output(eof, Expect, Port)
+ match_output(Acc, Expect, Port)
end.
-match_output([C|Output], [C|Expect], Port) ->
+match_output(<<C, Output/bits>>, <<C,Expect/bits>>, Port) ->
match_output(Output, Expect, Port);
-match_output([_|_], [_|_], Port) ->
+match_output(<<_, _/bits>>, <<_, _/bits>>, Port) ->
kill_port_and_fail(Port, badmatch);
-match_output([X|Output], [], Port) ->
- kill_port_and_fail(Port, {too_much_data, [X|Output]});
-match_output([], Expect, _Port) ->
- Expect;
-match_output(eof, [], _Port) ->
- [];
-match_output(eof, Expect, Port) ->
- kill_port_and_fail(Port, {unexpected_end_of_input, Expect}).
+match_output(<<_, _/bits>>=Rest, <<>>, Port) ->
+ kill_port_and_fail(Port, {too_much_data, Rest});
+match_output(<<>>, <<>>, _Port) ->
+ ok.
kill_port_and_fail(Port, Reason) ->
unlink(Port),
@@ -913,3 +892,137 @@ do_fd_leak(Bad, N) ->
io:format("Bad error after ~p attempts\n", [N]),
erlang:raise(C, R, Stk)
end.
+
+unicode(Config) ->
+ case file:native_name_encoding() of
+ latin1 ->
+ {comment, "Native name encoding is Latin-1; skipping all tests"};
+ utf8 ->
+ DataDir = proplists:get_value(data_dir, Config),
+ ok = file:set_cwd(proplists:get_value(priv_dir, Config)),
+ test_file_comment(DataDir),
+ test_archive_comment(DataDir),
+ test_bad_comment(DataDir),
+ test_latin1_archive(DataDir),
+ case has_zip() of
+ false ->
+ {comment, "No zip program found; skipping some tests"};
+ true ->
+ case zip_is_unicode_aware() of
+ true ->
+ test_filename_compatibility(),
+ ok;
+ false ->
+ {comment, "Old zip program; skipping some tests"}
+ end
+ end
+ end.
+
+test_filename_compatibility() ->
+ FancyName = "üñíĉòdë한",
+ Archive = "test.zip",
+
+ {ok, Archive} = zip:zip(Archive, [{FancyName, <<"test">>}]),
+ zipinfo_match(Archive, FancyName).
+
+test_file_comment(DataDir) ->
+ Archive = filename:join(DataDir, "zip_file_comment.zip"),
+ Comments = ["a", [246], [1024]],
+ FileNames = [[C] ++ ".txt" || C <- [$a, 246, 1024]],
+ [begin
+ test_zip_file(FileName, Comment, Archive),
+ test_file_comment(FileName, Comment, Archive)
+ end ||
+ Comment <- Comments, FileName <- FileNames],
+ ok.
+
+test_zip_file(FileName, Comment, Archive) ->
+ _ = file:delete(Archive),
+ io:format("*** zip:zip(). Testing FileName ~ts, Comment ~ts\n",
+ [FileName, Comment]),
+ ok = file:write_file(FileName, ["anything"]),
+ {ok, Archive} =
+ zip:zip(Archive, [FileName], [verbose, {comment, Comment}]),
+ zip_check(Archive, Comment, FileName, "").
+
+test_file_comment(FileName, Comment, Archive) ->
+ case test_zip1() of
+ false ->
+ ok;
+ true ->
+ _ = file:delete(Archive),
+ io:format("*** zip(1). Testing FileName ~ts, Comment ~ts\n",
+ [FileName, Comment]),
+ ok = file:write_file(FileName, ["anything"]),
+ R = os:cmd("echo " ++ Comment ++ "| zip -c " ++
+ Archive ++ " " ++ FileName),
+ io:format("os:cmd/1 returns ~lp\n", [R]),
+ zip_check(Archive, "", FileName, Comment)
+ end.
+
+test_archive_comment(DataDir) ->
+ Archive = filename:join(DataDir, "zip_archive_comment.zip"),
+ Chars = [$a, 246, 1024],
+ [test_archive_comment(Char, Archive) || Char <- Chars],
+ ok.
+
+test_archive_comment(Char, Archive) ->
+ case test_zip1() of
+ false ->
+ ok;
+ true ->
+ _ = file:delete(Archive),
+ FileName = "a.txt",
+ Comment = [Char],
+ io:format("*** Testing archive Comment ~ts\n", [Comment]),
+ ok = file:write_file(FileName, ["anything"]),
+
+ {ok, _} =
+ zip:zip(Archive, [FileName], [verbose, {comment, Comment}]),
+ Res = os:cmd("zip -z " ++ Archive),
+ io:format("os:cmd/1 returns ~lp\n", [Res]),
+ true = lists:member(Char, Res),
+
+ os:cmd("echo " ++ Comment ++ "| zip -z "++
+ Archive ++ " " ++ FileName),
+ zip_check(Archive, Comment, FileName, "")
+ end.
+
+test_zip1() ->
+ has_zip() andalso zip_is_unicode_aware().
+
+has_zip() ->
+ os:find_executable("zip") =/= false.
+
+zip_is_unicode_aware() ->
+ S = os:cmd("zip -v | grep 'UNICODE_SUPPORT'"),
+ string:find(S, "UNICODE_SUPPORT") =/= nomatch.
+
+zip_check(Archive, ArchiveComment, FileName, FileNameComment) ->
+ {ok, CommentAndFiles} = zip:table(Archive),
+ io:format("zip:table/1 returns\n ~lp\n", [CommentAndFiles]),
+ io:format("checking archive comment ~lp\n", [ArchiveComment]),
+ [_] = [C || #zip_comment{comment = C} <- CommentAndFiles,
+ C =:= ArchiveComment],
+ io:format("checking filename ~lp\n", [FileName]),
+ io:format("and filename comment ~lp\n", [FileNameComment]),
+ [_] = [F || #zip_file{name = F, comment = C} <- CommentAndFiles,
+ F =:= FileName, C =:= FileNameComment],
+ {ok, FileList} = zip:unzip(Archive, [verbose]),
+ io:format("zip:unzip/2 returns\n ~lp\n", [FileList]),
+ true = lists:member(FileName, FileList),
+ ok.
+
+test_bad_comment(DataDir) ->
+ Archive = filename:join(DataDir, "zip_bad_comment.zip"),
+ FileName = "a.txt",
+ file:write_file(FileName, ["something"]),
+ Comment = [9999999],
+ {error,{bad_unicode,Comment}} =
+ zip:zip(Archive, [FileName], [verbose, {comment, Comment}]).
+
+test_latin1_archive(DataDir) ->
+ Archive = filename:join(DataDir, "zip-latin1.zip"),
+ FileName = [246] ++ ".txt",
+ ArchiveComment = [246],
+ zip_check(Archive, ArchiveComment, FileName, "").
diff --git a/lib/stdlib/test/zip_SUITE_data/zip-latin1.zip b/lib/stdlib/test/zip_SUITE_data/zip-latin1.zip
new file mode 100644
index 0000000000..d54c783653
--- /dev/null
+++ b/lib/stdlib/test/zip_SUITE_data/zip-latin1.zip
Binary files differ
diff --git a/lib/stdlib/uc_spec/CaseFolding.txt b/lib/stdlib/uc_spec/CaseFolding.txt
index cce350f49c..7eeb915abf 100644
--- a/lib/stdlib/uc_spec/CaseFolding.txt
+++ b/lib/stdlib/uc_spec/CaseFolding.txt
@@ -1,6 +1,6 @@
-# CaseFolding-11.0.0.txt
-# Date: 2018-01-31, 08:20:09 GMT
-# © 2018 Unicode®, Inc.
+# CaseFolding-12.1.0.txt
+# Date: 2019-03-10, 10:53:00 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -1227,6 +1227,13 @@ A7B3; C; AB53; # LATIN CAPITAL LETTER CHI
A7B4; C; A7B5; # LATIN CAPITAL LETTER BETA
A7B6; C; A7B7; # LATIN CAPITAL LETTER OMEGA
A7B8; C; A7B9; # LATIN CAPITAL LETTER U WITH STROKE
+A7BA; C; A7BB; # LATIN CAPITAL LETTER GLOTTAL A
+A7BC; C; A7BD; # LATIN CAPITAL LETTER GLOTTAL I
+A7BE; C; A7BF; # LATIN CAPITAL LETTER GLOTTAL U
+A7C2; C; A7C3; # LATIN CAPITAL LETTER ANGLICANA W
+A7C4; C; A794; # LATIN CAPITAL LETTER C WITH PALATAL HOOK
+A7C5; C; 0282; # LATIN CAPITAL LETTER S WITH HOOK
+A7C6; C; 1D8E; # LATIN CAPITAL LETTER Z WITH PALATAL HOOK
AB70; C; 13A0; # CHEROKEE SMALL LETTER A
AB71; C; 13A1; # CHEROKEE SMALL LETTER E
AB72; C; 13A2; # CHEROKEE SMALL LETTER I
diff --git a/lib/stdlib/uc_spec/CompositionExclusions.txt b/lib/stdlib/uc_spec/CompositionExclusions.txt
index ea63595bd3..aa654974be 100644
--- a/lib/stdlib/uc_spec/CompositionExclusions.txt
+++ b/lib/stdlib/uc_spec/CompositionExclusions.txt
@@ -1,6 +1,6 @@
-# CompositionExclusions-11.0.0.txt
-# Date: 2017-12-06, 00:00:00 GMT [KW, LI]
-# © 2017 Unicode®, Inc.
+# CompositionExclusions-12.1.0.txt
+# Date: 2019-03-08, 23:59:00 GMT [KW, LI]
+# © 2019 Unicode®, Inc.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Unicode Character Database
diff --git a/lib/stdlib/uc_spec/GraphemeBreakProperty.txt b/lib/stdlib/uc_spec/GraphemeBreakProperty.txt
index 52052e6e33..b75b201f97 100644
--- a/lib/stdlib/uc_spec/GraphemeBreakProperty.txt
+++ b/lib/stdlib/uc_spec/GraphemeBreakProperty.txt
@@ -1,6 +1,6 @@
-# GraphemeBreakProperty-11.0.0.txt
-# Date: 2018-03-16, 20:34:02 GMT
-# © 2018 Unicode®, Inc.
+# GraphemeBreakProperty-12.1.0.txt
+# Date: 2019-03-10, 10:53:12 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -27,10 +27,10 @@
110CD ; Prepend # Cf KAITHI NUMBER SIGN ABOVE
111C2..111C3 ; Prepend # Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA
11A3A ; Prepend # Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA
-11A86..11A89 ; Prepend # Lo [4] SOYOMBO CLUSTER-INITIAL LETTER RA..SOYOMBO CLUSTER-INITIAL LETTER SA
+11A84..11A89 ; Prepend # Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA
11D46 ; Prepend # Lo MASARAM GONDI REPHA
-# Total code points: 20
+# Total code points: 22
# ================================================
@@ -61,10 +61,10 @@
2060..2064 ; Control # Cf [5] WORD JOINER..INVISIBLE PLUS
2065 ; Control # Cn <reserved-2065>
2066..206F ; Control # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
-D800..DFFF ; Control # Cs [2048] <surrogate-D800>..<surrogate-DFFF>
FEFF ; Control # Cf ZERO WIDTH NO-BREAK SPACE
FFF0..FFF8 ; Control # Cn [9] <reserved-FFF0>..<reserved-FFF8>
FFF9..FFFB ; Control # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+13430..13438 ; Control # Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
1BCA0..1BCA3 ; Control # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
1D173..1D17A ; Control # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
E0000 ; Control # Cn <reserved-E0000>
@@ -73,7 +73,7 @@ E0002..E001F ; Control # Cn [30] <reserved-E0002>..<reserved-E001F>
E0080..E00FF ; Control # Cn [128] <reserved-E0080>..<reserved-E00FF>
E01F0..E0FFF ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
-# Total code points: 5925
+# Total code points: 3886
# ================================================
@@ -178,8 +178,7 @@ E01F0..E0FFF ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
0E34..0E3A ; Extend # Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU
0E47..0E4E ; Extend # Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN
0EB1 ; Extend # Mn LAO VOWEL SIGN MAI KAN
-0EB4..0EB9 ; Extend # Mn [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU
-0EBB..0EBC ; Extend # Mn [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO
+0EB4..0EBC ; Extend # Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO
0EC8..0ECD ; Extend # Mn [6] LAO TONE MAI EK..LAO NIGGAHITA
0F18..0F19 ; Extend # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
0F35 ; Extend # Mn TIBETAN MARK NGAS BZUNG NYI ZLA
@@ -232,6 +231,7 @@ E01F0..E0FFF ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
1ABE ; Extend # Me COMBINING PARENTHESES OVERLAY
1B00..1B03 ; Extend # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
1B34 ; Extend # Mn BALINESE SIGN REREKAN
+1B35 ; Extend # Mc BALINESE VOWEL SIGN TEDUNG
1B36..1B3A ; Extend # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
1B3C ; Extend # Mn BALINESE VOWEL SIGN LA LENGA
1B42 ; Extend # Mn BALINESE VOWEL SIGN PEPET
@@ -283,7 +283,7 @@ A947..A951 ; Extend # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
A980..A982 ; Extend # Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR
A9B3 ; Extend # Mn JAVANESE SIGN CECAK TELU
A9B6..A9B9 ; Extend # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
-A9BC ; Extend # Mn JAVANESE VOWEL SIGN PEPET
+A9BC..A9BD ; Extend # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
A9E5 ; Extend # Mn MYANMAR SIGN SHAN SAW
AA29..AA2E ; Extend # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
AA31..AA32 ; Extend # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
@@ -368,6 +368,9 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
11727..1172B ; Extend # Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER
1182F..11837 ; Extend # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
11839..1183A ; Extend # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA
+119D4..119D7 ; Extend # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+119DA..119DB ; Extend # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+119E0 ; Extend # Mn NANDINAGARI SIGN VIRAMA
11A01..11A0A ; Extend # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
11A33..11A38 ; Extend # Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA
11A3B..11A3E ; Extend # Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA
@@ -394,6 +397,7 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
11EF3..11EF4 ; Extend # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
16AF0..16AF4 ; Extend # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
16B30..16B36 ; Extend # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+16F4F ; Extend # Mn MIAO SIGN CONSONANT MODIFIER BAR
16F8F..16F92 ; Extend # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
1BC9D..1BC9E ; Extend # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
1D165 ; Extend # Mc MUSICAL SYMBOL COMBINING STEM
@@ -414,13 +418,15 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
1E01B..1E021 ; Extend # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
1E023..1E024 ; Extend # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS
1E026..1E02A ; Extend # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA
+1E130..1E136 ; Extend # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+1E2EC..1E2EF ; Extend # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
1E8D0..1E8D6 ; Extend # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
1E944..1E94A ; Extend # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
1F3FB..1F3FF ; Extend # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
E0020..E007F ; Extend # Cf [96] TAG SPACE..CANCEL TAG
E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
-# Total code points: 1948
+# Total code points: 1970
# ================================================
@@ -489,7 +495,6 @@ E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
1A57 ; SpacingMark # Mc TAI THAM CONSONANT SIGN LA TANG LAI
1A6D..1A72 ; SpacingMark # Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI
1B04 ; SpacingMark # Mc BALINESE SIGN BISAH
-1B35 ; SpacingMark # Mc BALINESE VOWEL SIGN TEDUNG
1B3B ; SpacingMark # Mc BALINESE VOWEL SIGN RA REPA TEDUNG
1B3D..1B41 ; SpacingMark # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
1B43..1B44 ; SpacingMark # Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
@@ -504,7 +509,6 @@ E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
1C24..1C2B ; SpacingMark # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
1C34..1C35 ; SpacingMark # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
1CE1 ; SpacingMark # Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA
-1CF2..1CF3 ; SpacingMark # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA
1CF7 ; SpacingMark # Mc VEDIC SIGN ATIKRAMA
A823..A824 ; SpacingMark # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
A827 ; SpacingMark # Mc SYLOTI NAGRI VOWEL SIGN OO
@@ -514,7 +518,7 @@ A952..A953 ; SpacingMark # Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA
A983 ; SpacingMark # Mc JAVANESE SIGN WIGNYAN
A9B4..A9B5 ; SpacingMark # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
A9BA..A9BB ; SpacingMark # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
-A9BD..A9C0 ; SpacingMark # Mc [4] JAVANESE CONSONANT SIGN KERET..JAVANESE PANGKON
+A9BE..A9C0 ; SpacingMark # Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON
AA2F..AA30 ; SpacingMark # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
AA33..AA34 ; SpacingMark # Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
AA4D ; SpacingMark # Mc CHAM CONSONANT SIGN FINAL H
@@ -566,6 +570,9 @@ ABEC ; SpacingMark # Mc MEETEI MAYEK LUM IYEK
11726 ; SpacingMark # Mc AHOM VOWEL SIGN E
1182C..1182E ; SpacingMark # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
11838 ; SpacingMark # Mc DOGRA SIGN VISARGA
+119D1..119D3 ; SpacingMark # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+119DC..119DF ; SpacingMark # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+119E4 ; SpacingMark # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
11A39 ; SpacingMark # Mc ZANABAZAR SQUARE SIGN VISARGA
11A57..11A58 ; SpacingMark # Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU
11A97 ; SpacingMark # Mc SOYOMBO SIGN VISARGA
@@ -578,11 +585,11 @@ ABEC ; SpacingMark # Mc MEETEI MAYEK LUM IYEK
11D93..11D94 ; SpacingMark # Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU
11D96 ; SpacingMark # Mc GUNJALA GONDI SIGN VISARGA
11EF5..11EF6 ; SpacingMark # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
-16F51..16F7E ; SpacingMark # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG
+16F51..16F87 ; SpacingMark # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
1D166 ; SpacingMark # Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
1D16D ; SpacingMark # Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT
-# Total code points: 362
+# Total code points: 375
# ================================================
diff --git a/lib/stdlib/uc_spec/PropList.txt b/lib/stdlib/uc_spec/PropList.txt
index ef86795abe..4394602fea 100644
--- a/lib/stdlib/uc_spec/PropList.txt
+++ b/lib/stdlib/uc_spec/PropList.txt
@@ -1,6 +1,6 @@
-# PropList-11.0.0.txt
-# Date: 2018-03-15, 04:28:35 GMT
-# © 2018 Unicode®, Inc.
+# PropList-12.1.0.txt
+# Date: 2019-03-10, 10:53:16 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -138,7 +138,7 @@ FF63 ; Quotation_Mark # Pe HALFWIDTH RIGHT CORNER BRACKET
0F0D..0F12 ; Terminal_Punctuation # Po [6] TIBETAN MARK SHAD..TIBETAN MARK RGYA GRAM SHAD
104A..104B ; Terminal_Punctuation # Po [2] MYANMAR SIGN LITTLE SECTION..MYANMAR SIGN SECTION
1361..1368 ; Terminal_Punctuation # Po [8] ETHIOPIC WORDSPACE..ETHIOPIC PARAGRAPH SEPARATOR
-166D..166E ; Terminal_Punctuation # Po [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP
+166E ; Terminal_Punctuation # Po CANADIAN SYLLABICS FULL STOP
16EB..16ED ; Terminal_Punctuation # Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION
1735..1736 ; Terminal_Punctuation # Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION
17D4..17D6 ; Terminal_Punctuation # Po [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH
@@ -157,7 +157,7 @@ FF63 ; Quotation_Mark # Pe HALFWIDTH RIGHT CORNER BRACKET
2E3C ; Terminal_Punctuation # Po STENOGRAPHIC FULL STOP
2E41 ; Terminal_Punctuation # Po REVERSED COMMA
2E4C ; Terminal_Punctuation # Po MEDIEVAL COMMA
-2E4E ; Terminal_Punctuation # Po PUNCTUS ELEVATUS MARK
+2E4E..2E4F ; Terminal_Punctuation # Po [2] PUNCTUS ELEVATUS MARK..CORNISH VERSE DIVIDER
3001..3002 ; Terminal_Punctuation # Po [2] IDEOGRAPHIC COMMA..IDEOGRAPHIC FULL STOP
A4FE..A4FF ; Terminal_Punctuation # Po [2] LISU PUNCTUATION COMMA..LISU PUNCTUATION FULL STOP
A60D..A60F ; Terminal_Punctuation # Po [3] VAI COMMA..VAI QUESTION MARK
@@ -553,15 +553,17 @@ FF41..FF46 ; Hex_Digit # L& [6] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L
1056..1057 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
1058..1059 ; Other_Alphabetic # Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
105E..1060 ; Other_Alphabetic # Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
-1062 ; Other_Alphabetic # Mc MYANMAR VOWEL SIGN SGAW KAREN EU
-1067..1068 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR VOWEL SIGN WESTERN PWO KAREN UE
+1062..1064 ; Other_Alphabetic # Mc [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO
+1067..106D ; Other_Alphabetic # Mc [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5
1071..1074 ; Other_Alphabetic # Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
1082 ; Other_Alphabetic # Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA
1083..1084 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E
1085..1086 ; Other_Alphabetic # Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
-109C ; Other_Alphabetic # Mc MYANMAR VOWEL SIGN AITON A
+1087..108C ; Other_Alphabetic # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
+108D ; Other_Alphabetic # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+108F ; Other_Alphabetic # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5
+109A..109C ; Other_Alphabetic # Mc [3] MYANMAR SIGN KHAMTI TONE-1..MYANMAR VOWEL SIGN AITON A
109D ; Other_Alphabetic # Mn MYANMAR VOWEL SIGN AITON AI
-135F ; Other_Alphabetic # Mn ETHIOPIC COMBINING GEMINATION MARK
1712..1713 ; Other_Alphabetic # Mn [2] TAGALOG VOWEL SIGN I..TAGALOG VOWEL SIGN U
1732..1733 ; Other_Alphabetic # Mn [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U
1752..1753 ; Other_Alphabetic # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
@@ -618,18 +620,21 @@ FF41..FF46 ; Hex_Digit # L& [6] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L
1C24..1C2B ; Other_Alphabetic # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
1C2C..1C33 ; Other_Alphabetic # Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
1C34..1C35 ; Other_Alphabetic # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
-1CF2..1CF3 ; Other_Alphabetic # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA
+1C36 ; Other_Alphabetic # Mn LEPCHA SIGN RAN
1DE7..1DF4 ; Other_Alphabetic # Mn [14] COMBINING LATIN SMALL LETTER ALPHA..COMBINING LATIN SMALL LETTER U WITH DIAERESIS
24B6..24E9 ; Other_Alphabetic # So [52] CIRCLED LATIN CAPITAL LETTER A..CIRCLED LATIN SMALL LETTER Z
2DE0..2DFF ; Other_Alphabetic # Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
A674..A67B ; Other_Alphabetic # Mn [8] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC LETTER OMEGA
A69E..A69F ; Other_Alphabetic # Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E
+A802 ; Other_Alphabetic # Mn SYLOTI NAGRI SIGN DVISVARA
+A80B ; Other_Alphabetic # Mn SYLOTI NAGRI SIGN ANUSVARA
A823..A824 ; Other_Alphabetic # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
A825..A826 ; Other_Alphabetic # Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
A827 ; Other_Alphabetic # Mc SYLOTI NAGRI VOWEL SIGN OO
A880..A881 ; Other_Alphabetic # Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
A8B4..A8C3 ; Other_Alphabetic # Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
A8C5 ; Other_Alphabetic # Mn SAURASHTRA SIGN CANDRABINDU
+A8FF ; Other_Alphabetic # Mn DEVANAGARI VOWEL SIGN AY
A926..A92A ; Other_Alphabetic # Mn [5] KAYAH LI VOWEL UE..KAYAH LI VOWEL O
A947..A951 ; Other_Alphabetic # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
A952 ; Other_Alphabetic # Mc REJANG CONSONANT SIGN H
@@ -638,8 +643,9 @@ A983 ; Other_Alphabetic # Mc JAVANESE SIGN WIGNYAN
A9B4..A9B5 ; Other_Alphabetic # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
A9B6..A9B9 ; Other_Alphabetic # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
A9BA..A9BB ; Other_Alphabetic # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
-A9BC ; Other_Alphabetic # Mn JAVANESE VOWEL SIGN PEPET
-A9BD..A9BF ; Other_Alphabetic # Mc [3] JAVANESE CONSONANT SIGN KERET..JAVANESE CONSONANT SIGN CAKRA
+A9BC..A9BD ; Other_Alphabetic # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
+A9BE..A9BF ; Other_Alphabetic # Mc [2] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE CONSONANT SIGN CAKRA
+A9E5 ; Other_Alphabetic # Mn MYANMAR SIGN SHAN SAW
AA29..AA2E ; Other_Alphabetic # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
AA2F..AA30 ; Other_Alphabetic # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
AA31..AA32 ; Other_Alphabetic # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
@@ -648,6 +654,9 @@ AA35..AA36 ; Other_Alphabetic # Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONA
AA43 ; Other_Alphabetic # Mn CHAM CONSONANT SIGN FINAL NG
AA4C ; Other_Alphabetic # Mn CHAM CONSONANT SIGN FINAL M
AA4D ; Other_Alphabetic # Mc CHAM CONSONANT SIGN FINAL H
+AA7B ; Other_Alphabetic # Mc MYANMAR SIGN PAO KAREN TONE
+AA7C ; Other_Alphabetic # Mn MYANMAR SIGN TAI LAING TONE-2
+AA7D ; Other_Alphabetic # Mc MYANMAR SIGN TAI LAING TONE-5
AAB0 ; Other_Alphabetic # Mn TAI VIET MAI KANG
AAB2..AAB4 ; Other_Alphabetic # Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U
AAB7..AAB8 ; Other_Alphabetic # Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA
@@ -740,6 +749,11 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
1182C..1182E ; Other_Alphabetic # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
1182F..11837 ; Other_Alphabetic # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
11838 ; Other_Alphabetic # Mc DOGRA SIGN VISARGA
+119D1..119D3 ; Other_Alphabetic # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+119D4..119D7 ; Other_Alphabetic # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+119DA..119DB ; Other_Alphabetic # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+119DC..119DF ; Other_Alphabetic # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+119E4 ; Other_Alphabetic # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
11A01..11A0A ; Other_Alphabetic # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
11A35..11A38 ; Other_Alphabetic # Mn [4] ZANABAZAR SQUARE SIGN CANDRABINDU..ZANABAZAR SQUARE SIGN ANUSVARA
11A39 ; Other_Alphabetic # Mc ZANABAZAR SQUARE SIGN VISARGA
@@ -773,8 +787,9 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
11D96 ; Other_Alphabetic # Mc GUNJALA GONDI SIGN VISARGA
11EF3..11EF4 ; Other_Alphabetic # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
11EF5..11EF6 ; Other_Alphabetic # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
-16B30..16B36 ; Other_Alphabetic # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
-16F51..16F7E ; Other_Alphabetic # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG
+16F4F ; Other_Alphabetic # Mn MIAO SIGN CONSONANT MODIFIER BAR
+16F51..16F87 ; Other_Alphabetic # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
+16F8F..16F92 ; Other_Alphabetic # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
1BC9E ; Other_Alphabetic # Mn DUPLOYAN DOUBLE MARK
1E000..1E006 ; Other_Alphabetic # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
1E008..1E018 ; Other_Alphabetic # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
@@ -786,7 +801,7 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
1F150..1F169 ; Other_Alphabetic # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z
1F170..1F189 ; Other_Alphabetic # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z
-# Total code points: 1334
+# Total code points: 1377
# ================================================
@@ -798,7 +813,7 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
4E00..9FEF ; Ideographic # Lo [20976] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FEF
F900..FA6D ; Ideographic # Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA6D
FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9
-17000..187F1 ; Ideographic # Lo [6130] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F1
+17000..187F7 ; Ideographic # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
18800..18AF2 ; Ideographic # Lo [755] TANGUT COMPONENT-001..TANGUT COMPONENT-755
1B170..1B2FB ; Ideographic # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
20000..2A6D6 ; Ideographic # Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6
@@ -808,7 +823,7 @@ FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COM
2CEB0..2EBE0 ; Ideographic # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
2F800..2FA1D ; Ideographic # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
-# Total code points: 96184
+# Total code points: 96190
# ================================================
@@ -876,6 +891,7 @@ FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COM
0DCA ; Diacritic # Mn SINHALA SIGN AL-LAKUNA
0E47..0E4C ; Diacritic # Mn [6] THAI CHARACTER MAITAIKHU..THAI CHARACTER THANTHAKHAT
0E4E ; Diacritic # Mn THAI CHARACTER YAMAKKAN
+0EBA ; Diacritic # Mn LAO SIGN PALI VIRAMA
0EC8..0ECC ; Diacritic # Mn [5] LAO TONE MAI EK..LAO CANCELLATION MARK
0F18..0F19 ; Diacritic # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
0F35 ; Diacritic # Mn TIBETAN MARK NGAS BZUNG NYI ZLA
@@ -887,10 +903,13 @@ FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COM
0FC6 ; Diacritic # Mn TIBETAN SYMBOL PADMA GDAN
1037 ; Diacritic # Mn MYANMAR SIGN DOT BELOW
1039..103A ; Diacritic # Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+1063..1064 ; Diacritic # Mc [2] MYANMAR TONE MARK SGAW KAREN HATHI..MYANMAR TONE MARK SGAW KAREN KE PHO
+1069..106D ; Diacritic # Mc [5] MYANMAR SIGN WESTERN PWO KAREN TONE-1..MYANMAR SIGN WESTERN PWO KAREN TONE-5
1087..108C ; Diacritic # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
108D ; Diacritic # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
108F ; Diacritic # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5
109A..109B ; Diacritic # Mc [2] MYANMAR SIGN KHAMTI TONE-1..MYANMAR SIGN KHAMTI TONE-3
+135D..135F ; Diacritic # Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK
17C9..17D3 ; Diacritic # Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
17DD ; Diacritic # Mn KHMER SIGN ATTHACAN
1939..193B ; Diacritic # Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I
@@ -935,9 +954,11 @@ A67C..A67D ; Diacritic # Mn [2] COMBINING CYRILLIC KAVYKA..COMBINING CYRILL
A67F ; Diacritic # Lm CYRILLIC PAYEROK
A69C..A69D ; Diacritic # Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN
A6F0..A6F1 ; Diacritic # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS
+A700..A716 ; Diacritic # Sk [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR
A717..A71F ; Diacritic # Lm [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK
A720..A721 ; Diacritic # Sk [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE
A788 ; Diacritic # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT
+A789..A78A ; Diacritic # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
A7F8..A7F9 ; Diacritic # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
A8C4 ; Diacritic # Mn SAURASHTRA SIGN VIRAMA
A8E0..A8F1 ; Diacritic # Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA
@@ -992,6 +1013,7 @@ FFE3 ; Diacritic # Sk FULLWIDTH MACRON
116B7 ; Diacritic # Mn TAKRI SIGN NUKTA
1172B ; Diacritic # Mn AHOM SIGN KILLER
11839..1183A ; Diacritic # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA
+119E0 ; Diacritic # Mn NANDINAGARI SIGN VIRAMA
11A34 ; Diacritic # Mn ZANABAZAR SQUARE SIGN VIRAMA
11A47 ; Diacritic # Mn ZANABAZAR SQUARE SUBJOINER
11A99 ; Diacritic # Mn SOYOMBO SUBJOINER
@@ -1000,6 +1022,7 @@ FFE3 ; Diacritic # Sk FULLWIDTH MACRON
11D44..11D45 ; Diacritic # Mn [2] MASARAM GONDI SIGN HALANTA..MASARAM GONDI VIRAMA
11D97 ; Diacritic # Mn GUNJALA GONDI VIRAMA
16AF0..16AF4 ; Diacritic # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
+16B30..16B36 ; Diacritic # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
16F8F..16F92 ; Diacritic # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
16F93..16F9F ; Diacritic # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
1D167..1D169 ; Diacritic # Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
@@ -1007,11 +1030,13 @@ FFE3 ; Diacritic # Sk FULLWIDTH MACRON
1D17B..1D182 ; Diacritic # Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
1D185..1D18B ; Diacritic # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
1D1AA..1D1AD ; Diacritic # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+1E130..1E136 ; Diacritic # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+1E2EC..1E2EF ; Diacritic # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
1E8D0..1E8D6 ; Diacritic # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
1E944..1E946 ; Diacritic # Mn [3] ADLAM ALIF LENGTHENER..ADLAM GEMINATION MARK
1E948..1E94A ; Diacritic # Mn [3] ADLAM CONSONANT MODIFIER..ADLAM NUKTA
-# Total code points: 818
+# Total code points: 873
# ================================================
@@ -1043,9 +1068,11 @@ FF70 ; Extender # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND
11A98 ; Extender # Mn SOYOMBO GEMINATION MARK
16B42..16B43 ; Extender # Lm [2] PAHAWH HMONG SIGN VOS NRUA..PAHAWH HMONG SIGN IB YAM
16FE0..16FE1 ; Extender # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
+16FE3 ; Extender # Lm OLD CHINESE ITERATION MARK
+1E13C..1E13D ; Extender # Lm [2] NYIAKENG PUACHUE HMONG SIGN XW XW..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
1E944..1E946 ; Extender # Mn [3] ADLAM ALIF LENGTHENER..ADLAM GEMINATION MARK
-# Total code points: 44
+# Total code points: 47
# ================================================
@@ -1119,6 +1146,7 @@ FFFFE..FFFFF ; Noncharacter_Code_Point # Cn [2] <noncharacter-FFFFE>..<noncha
0D57 ; Other_Grapheme_Extend # Mc MALAYALAM AU LENGTH MARK
0DCF ; Other_Grapheme_Extend # Mc SINHALA VOWEL SIGN AELA-PILLA
0DDF ; Other_Grapheme_Extend # Mc SINHALA VOWEL SIGN GAYANUKITTA
+1B35 ; Other_Grapheme_Extend # Mc BALINESE VOWEL SIGN TEDUNG
200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER
302E..302F ; Other_Grapheme_Extend # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK
FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
@@ -1131,7 +1159,7 @@ FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND
1D16E..1D172 ; Other_Grapheme_Extend # Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5
E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG
-# Total code points: 125
+# Total code points: 126
# ================================================
@@ -1547,10 +1575,7 @@ E0100..E01EF ; Variation_Selector # Mn [240] VARIATION SELECTOR-17..VARIATION S
2B74..2B75 ; Pattern_Syntax # Cn [2] <reserved-2B74>..<reserved-2B75>
2B76..2B95 ; Pattern_Syntax # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW
2B96..2B97 ; Pattern_Syntax # Cn [2] <reserved-2B96>..<reserved-2B97>
-2B98..2BC8 ; Pattern_Syntax # So [49] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED
-2BC9 ; Pattern_Syntax # Cn <reserved-2BC9>
-2BCA..2BFE ; Pattern_Syntax # So [53] TOP HALF BLACK CIRCLE..REVERSED RIGHT ANGLE
-2BFF ; Pattern_Syntax # Cn <reserved-2BFF>
+2B98..2BFF ; Pattern_Syntax # So [104] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..HELLSCHREIBER PAUSE SYMBOL
2E00..2E01 ; Pattern_Syntax # Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER
2E02 ; Pattern_Syntax # Pi LEFT SUBSTITUTION BRACKET
2E03 ; Pattern_Syntax # Pf RIGHT SUBSTITUTION BRACKET
@@ -1588,8 +1613,8 @@ E0100..E01EF ; Variation_Selector # Mn [240] VARIATION SELECTOR-17..VARIATION S
2E40 ; Pattern_Syntax # Pd DOUBLE HYPHEN
2E41 ; Pattern_Syntax # Po REVERSED COMMA
2E42 ; Pattern_Syntax # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK
-2E43..2E4E ; Pattern_Syntax # Po [12] DASH WITH LEFT UPTURN..PUNCTUS ELEVATUS MARK
-2E4F..2E7F ; Pattern_Syntax # Cn [49] <reserved-2E4F>..<reserved-2E7F>
+2E43..2E4F ; Pattern_Syntax # Po [13] DASH WITH LEFT UPTURN..CORNISH VERSE DIVIDER
+2E50..2E7F ; Pattern_Syntax # Cn [48] <reserved-2E50>..<reserved-2E7F>
3001..3003 ; Pattern_Syntax # Po [3] IDEOGRAPHIC COMMA..DITTO MARK
3008 ; Pattern_Syntax # Ps LEFT ANGLE BRACKET
3009 ; Pattern_Syntax # Pe RIGHT ANGLE BRACKET
diff --git a/lib/stdlib/uc_spec/README-UPDATE.txt b/lib/stdlib/uc_spec/README-UPDATE.txt
index e1f5c8fcd0..73274e512a 100644
--- a/lib/stdlib/uc_spec/README-UPDATE.txt
+++ b/lib/stdlib/uc_spec/README-UPDATE.txt
@@ -2,12 +2,10 @@ When updating the unicode version copy the necessary files to this
directory.
And update the test files in stdlib/test/unicode_util_SUITE_data/*
-Unicode 11 was updated from:
-https://www.unicode.org/Public/11.0.0/ucd/
-https://www.unicode.org/Public/11.0.0/ucd/auxiliary/
-https://www.unicode.org/Public/emoji/11.0/
+Unicode 12.1 was updated from:
+https://www.unicode.org/Public/12.1.0/ucd/
+https://www.unicode.org/Public/12.1.0/ucd/auxiliary/
+https://www.unicode.org/Public/emoji/12.0/
Update the spec_version(..) function in the generator,
gen_unicode_mod.escript
-
-
diff --git a/lib/stdlib/uc_spec/SpecialCasing.txt b/lib/stdlib/uc_spec/SpecialCasing.txt
index c90d09acb3..1c04aacf97 100644
--- a/lib/stdlib/uc_spec/SpecialCasing.txt
+++ b/lib/stdlib/uc_spec/SpecialCasing.txt
@@ -1,6 +1,6 @@
-# SpecialCasing-11.0.0.txt
-# Date: 2018-02-22, 06:16:47 GMT
-# © 2018 Unicode®, Inc.
+# SpecialCasing-12.1.0.txt
+# Date: 2019-03-10, 10:53:28 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
diff --git a/lib/stdlib/uc_spec/UnicodeData.txt b/lib/stdlib/uc_spec/UnicodeData.txt
index ec32fafbce..e65aec52f7 100644
--- a/lib/stdlib/uc_spec/UnicodeData.txt
+++ b/lib/stdlib/uc_spec/UnicodeData.txt
@@ -640,7 +640,7 @@
027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;;
0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;;01A6;;01A6
0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;;
-0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;;
+0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;A7C5;;A7C5
0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9
0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;;
0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;;
@@ -2809,6 +2809,7 @@
0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0C77;TELUGU SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
0C78;TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR;No;0;ON;;;;0;N;;;;;
0C79;TELUGU FRACTION DIGIT ONE FOR ODD POWERS OF FOUR;No;0;ON;;;;1;N;;;;;
0C7A;TELUGU FRACTION DIGIT TWO FOR ODD POWERS OF FOUR;No;0;ON;;;;2;N;;;;;
@@ -3203,14 +3204,24 @@
0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;;
0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;;
0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;;
+0E86;LAO LETTER PALI GHA;Lo;0;L;;;;;N;;;;;
0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;;
0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;;
+0E89;LAO LETTER PALI CHA;Lo;0;L;;;;;N;;;;;
0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;;
+0E8C;LAO LETTER PALI JHA;Lo;0;L;;;;;N;;;;;
0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;;
+0E8E;LAO LETTER PALI NYA;Lo;0;L;;;;;N;;;;;
+0E8F;LAO LETTER PALI TTA;Lo;0;L;;;;;N;;;;;
+0E90;LAO LETTER PALI TTHA;Lo;0;L;;;;;N;;;;;
+0E91;LAO LETTER PALI DDA;Lo;0;L;;;;;N;;;;;
+0E92;LAO LETTER PALI DDHA;Lo;0;L;;;;;N;;;;;
+0E93;LAO LETTER PALI NNA;Lo;0;L;;;;;N;;;;;
0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;;
0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;;
0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;;
0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;;
+0E98;LAO LETTER PALI DHA;Lo;0;L;;;;;N;;;;;
0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;;
0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;;
0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;;
@@ -3218,13 +3229,17 @@
0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;;
0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;;
0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;;
+0EA0;LAO LETTER PALI BHA;Lo;0;L;;;;;N;;;;;
0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;;
0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;;
0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;;
0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;;
0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;;
+0EA8;LAO LETTER SANSKRIT SHA;Lo;0;L;;;;;N;;;;;
+0EA9;LAO LETTER SANSKRIT SSA;Lo;0;L;;;;;N;;;;;
0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;;
0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;;
+0EAC;LAO LETTER PALI LLA;Lo;0;L;;;;;N;;;;;
0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;;
0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;;
0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;;
@@ -3238,6 +3253,7 @@
0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;;
0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;;
+0EBA;LAO SIGN PALI VIRAMA;Mn;9;NSM;;;;;N;;;;;
0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;;
0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;;
0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;;
@@ -5079,7 +5095,7 @@
166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;;
166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;;
166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;;
-166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;;
+166D;CANADIAN SYLLABICS CHI SIGN;So;0;L;;;;;N;;;;;
166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;;
166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;;
1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;;
@@ -6488,14 +6504,15 @@
1CEF;VEDIC SIGN LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
1CF0;VEDIC SIGN RTHANG LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
1CF1;VEDIC SIGN ANUSVARA UBHAYATO MUKHA;Lo;0;L;;;;;N;;;;;
-1CF2;VEDIC SIGN ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
-1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
+1CF2;VEDIC SIGN ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
+1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
1CF4;VEDIC TONE CANDRA ABOVE;Mn;230;NSM;;;;;N;;;;;
1CF5;VEDIC SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
1CF6;VEDIC SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
1CF7;VEDIC SIGN ATIKRAMA;Mc;0;L;;;;;N;;;;;
1CF8;VEDIC TONE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
1CF9;VEDIC TONE DOUBLE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+1CFA;VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA;Lo;0;L;;;;;N;;;;;
1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;;
1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;;
1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;;
@@ -6638,7 +6655,7 @@
1D8B;LATIN SMALL LETTER ESH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
1D8C;LATIN SMALL LETTER V WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
1D8D;LATIN SMALL LETTER X WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
-1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C6;;A7C6
1D8F;LATIN SMALL LETTER A WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
1D90;LATIN SMALL LETTER ALPHA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
1D91;LATIN SMALL LETTER D WITH HOOK AND TAIL;Ll;0;L;;;;;N;;;;;
@@ -10165,6 +10182,7 @@
2BC6;BLACK MEDIUM DOWN-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
2BC7;BLACK MEDIUM LEFT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
2BC8;BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
+2BC9;NEPTUNE FORM TWO;So;0;ON;;;;;N;;;;;
2BCA;TOP HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
2BCB;BOTTOM HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
2BCC;LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;;
@@ -10218,6 +10236,7 @@
2BFC;DOUBLED SYMBOL;So;0;ON;;;;;N;;;;;
2BFD;PASSED SYMBOL;So;0;ON;;;;;N;;;;;
2BFE;REVERSED RIGHT ANGLE;So;0;ON;;;;;Y;;;;;
+2BFF;HELLSCHREIBER PAUSE SYMBOL;So;0;ON;;;;;N;;;;;
2C00;GLAGOLITIC CAPITAL LETTER AZU;Lu;0;L;;;;;N;;;;2C30;
2C01;GLAGOLITIC CAPITAL LETTER BUKY;Lu;0;L;;;;;N;;;;2C31;
2C02;GLAGOLITIC CAPITAL LETTER VEDE;Lu;0;L;;;;;N;;;;2C32;
@@ -10756,6 +10775,7 @@
2E4C;MEDIEVAL COMMA;Po;0;ON;;;;;N;;;;;
2E4D;PARAGRAPHUS MARK;Po;0;ON;;;;;N;;;;;
2E4E;PUNCTUS ELEVATUS MARK;Po;0;ON;;;;;N;;;;;
+2E4F;CORNISH VERSE DIVIDER;Po;0;ON;;;;;N;;;;;
2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
@@ -11836,6 +11856,7 @@
32FC;CIRCLED KATAKANA WI;So;0;L;<circle> 30F0;;;;N;;;;;
32FD;CIRCLED KATAKANA WE;So;0;L;<circle> 30F1;;;;N;;;;;
32FE;CIRCLED KATAKANA WO;So;0;L;<circle> 30F2;;;;N;;;;;
+32FF;SQUARE ERA NAME REIWA;So;0;L;<square> 4EE4 548C;;;;N;;;;;
3300;SQUARE APAATO;So;0;L;<square> 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;;
3301;SQUARE ARUHUA;So;0;L;<square> 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;;
3302;SQUARE ANPEA;So;0;L;<square> 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;;
@@ -14060,7 +14081,7 @@ A790;LATIN CAPITAL LETTER N WITH DESCENDER;Lu;0;L;;;;;N;;;;A791;
A791;LATIN SMALL LETTER N WITH DESCENDER;Ll;0;L;;;;;N;;;A790;;A790
A792;LATIN CAPITAL LETTER C WITH BAR;Lu;0;L;;;;;N;;;;A793;
A793;LATIN SMALL LETTER C WITH BAR;Ll;0;L;;;;;N;;;A792;;A792
-A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C4;;A7C4
A795;LATIN SMALL LETTER H WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
A796;LATIN CAPITAL LETTER B WITH FLOURISH;Lu;0;L;;;;;N;;;;A797;
A797;LATIN SMALL LETTER B WITH FLOURISH;Ll;0;L;;;;;N;;;A796;;A796
@@ -14098,6 +14119,17 @@ A7B6;LATIN CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;A7B7;
A7B7;LATIN SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;A7B6;;A7B6
A7B8;LATIN CAPITAL LETTER U WITH STROKE;Lu;0;L;;;;;N;;;;A7B9;
A7B9;LATIN SMALL LETTER U WITH STROKE;Ll;0;L;;;;;N;;;A7B8;;A7B8
+A7BA;LATIN CAPITAL LETTER GLOTTAL A;Lu;0;L;;;;;N;;;;A7BB;
+A7BB;LATIN SMALL LETTER GLOTTAL A;Ll;0;L;;;;;N;;;A7BA;;A7BA
+A7BC;LATIN CAPITAL LETTER GLOTTAL I;Lu;0;L;;;;;N;;;;A7BD;
+A7BD;LATIN SMALL LETTER GLOTTAL I;Ll;0;L;;;;;N;;;A7BC;;A7BC
+A7BE;LATIN CAPITAL LETTER GLOTTAL U;Lu;0;L;;;;;N;;;;A7BF;
+A7BF;LATIN SMALL LETTER GLOTTAL U;Ll;0;L;;;;;N;;;A7BE;;A7BE
+A7C2;LATIN CAPITAL LETTER ANGLICANA W;Lu;0;L;;;;;N;;;;A7C3;
+A7C3;LATIN SMALL LETTER ANGLICANA W;Ll;0;L;;;;;N;;;A7C2;;A7C2
+A7C4;LATIN CAPITAL LETTER C WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;A794;
+A7C5;LATIN CAPITAL LETTER S WITH HOOK;Lu;0;L;;;;;N;;;;0282;
+A7C6;LATIN CAPITAL LETTER Z WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;1D8E;
A7F7;LATIN EPIGRAPHIC LETTER SIDEWAYS I;Lo;0;L;;;;;N;;;;;
A7F8;MODIFIER LETTER CAPITAL H WITH STROKE;Lm;0;L;<super> 0126;;;;N;;;;;
A7F9;MODIFIER LETTER SMALL LIGATURE OE;Lm;0;L;<super> 0153;;;;N;;;;;
@@ -14506,7 +14538,7 @@ A9B9;JAVANESE VOWEL SIGN SUKU MENDUT;Mn;0;NSM;;;;;N;;;;;
A9BA;JAVANESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;;
A9BB;JAVANESE VOWEL SIGN DIRGA MURE;Mc;0;L;;;;;N;;;;;
A9BC;JAVANESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;;
-A9BD;JAVANESE CONSONANT SIGN KERET;Mc;0;L;;;;;N;;;;;
+A9BD;JAVANESE CONSONANT SIGN KERET;Mn;0;NSM;;;;;N;;;;;
A9BE;JAVANESE CONSONANT SIGN PENGKAL;Mc;0;L;;;;;N;;;;;
A9BF;JAVANESE CONSONANT SIGN CAKRA;Mc;0;L;;;;;N;;;;;
A9C0;JAVANESE PANGKON;Mc;9;L;;;;;N;;;;;
@@ -14863,6 +14895,8 @@ AB62;LATIN SMALL LETTER OPEN OE;Ll;0;L;;;;;N;;;;;
AB63;LATIN SMALL LETTER UO;Ll;0;L;;;;;N;;;;;
AB64;LATIN SMALL LETTER INVERTED ALPHA;Ll;0;L;;;;;N;;;;;
AB65;GREEK LETTER SMALL CAPITAL OMEGA;Ll;0;L;;;;;N;;;;;
+AB66;LATIN SMALL LETTER DZ DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+AB67;LATIN SMALL LETTER TS DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
AB70;CHEROKEE SMALL LETTER A;Ll;0;L;;;;;N;;;13A0;;13A0
AB71;CHEROKEE SMALL LETTER E;Ll;0;L;;;;;N;;;13A1;;13A1
AB72;CHEROKEE SMALL LETTER I;Ll;0;L;;;;;N;;;13A2;;13A2
@@ -19105,6 +19139,29 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10F57;SOGDIAN PUNCTUATION CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
10F58;SOGDIAN PUNCTUATION TWO CIRCLES WITH DOTS;Po;0;AL;;;;;N;;;;;
10F59;SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
+10FE0;ELYMAIC LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10FE1;ELYMAIC LETTER BETH;Lo;0;R;;;;;N;;;;;
+10FE2;ELYMAIC LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10FE3;ELYMAIC LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10FE4;ELYMAIC LETTER HE;Lo;0;R;;;;;N;;;;;
+10FE5;ELYMAIC LETTER WAW;Lo;0;R;;;;;N;;;;;
+10FE6;ELYMAIC LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10FE7;ELYMAIC LETTER HETH;Lo;0;R;;;;;N;;;;;
+10FE8;ELYMAIC LETTER TETH;Lo;0;R;;;;;N;;;;;
+10FE9;ELYMAIC LETTER YODH;Lo;0;R;;;;;N;;;;;
+10FEA;ELYMAIC LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10FEB;ELYMAIC LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10FEC;ELYMAIC LETTER MEM;Lo;0;R;;;;;N;;;;;
+10FED;ELYMAIC LETTER NUN;Lo;0;R;;;;;N;;;;;
+10FEE;ELYMAIC LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10FEF;ELYMAIC LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10FF0;ELYMAIC LETTER PE;Lo;0;R;;;;;N;;;;;
+10FF1;ELYMAIC LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10FF2;ELYMAIC LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10FF3;ELYMAIC LETTER RESH;Lo;0;R;;;;;N;;;;;
+10FF4;ELYMAIC LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10FF5;ELYMAIC LETTER TAW;Lo;0;R;;;;;N;;;;;
+10FF6;ELYMAIC LIGATURE ZAYIN-YODH;Lo;0;R;;;;;N;;;;;
11000;BRAHMI SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;;
11001;BRAHMI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
11002;BRAHMI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
@@ -19887,6 +19944,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1145B;NEWA PLACEHOLDER MARK;Po;0;L;;;;;N;;;;;
1145D;NEWA INSERTION SIGN;Po;0;L;;;;;N;;;;;
1145E;NEWA SANDHI MARK;Mn;230;NSM;;;;;N;;;;;
+1145F;NEWA LETTER VEDIC ANUSVARA;Lo;0;L;;;;;N;;;;;
11480;TIRHUTA ANJI;Lo;0;L;;;;;N;;;;;
11481;TIRHUTA LETTER A;Lo;0;L;;;;;N;;;;;
11482;TIRHUTA LETTER AA;Lo;0;L;;;;;N;;;;;
@@ -20209,6 +20267,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
116B5;TAKRI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+116B8;TAKRI LETTER ARCHAIC KHA;Lo;0;L;;;;;N;;;;;
116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
@@ -20421,6 +20480,71 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
118F1;WARANG CITI NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
118F2;WARANG CITI NUMBER NINETY;No;0;L;;;;90;N;;;;;
118FF;WARANG CITI OM;Lo;0;L;;;;;N;;;;;
+119A0;NANDINAGARI LETTER A;Lo;0;L;;;;;N;;;;;
+119A1;NANDINAGARI LETTER AA;Lo;0;L;;;;;N;;;;;
+119A2;NANDINAGARI LETTER I;Lo;0;L;;;;;N;;;;;
+119A3;NANDINAGARI LETTER II;Lo;0;L;;;;;N;;;;;
+119A4;NANDINAGARI LETTER U;Lo;0;L;;;;;N;;;;;
+119A5;NANDINAGARI LETTER UU;Lo;0;L;;;;;N;;;;;
+119A6;NANDINAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+119A7;NANDINAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+119AA;NANDINAGARI LETTER E;Lo;0;L;;;;;N;;;;;
+119AB;NANDINAGARI LETTER AI;Lo;0;L;;;;;N;;;;;
+119AC;NANDINAGARI LETTER O;Lo;0;L;;;;;N;;;;;
+119AD;NANDINAGARI LETTER AU;Lo;0;L;;;;;N;;;;;
+119AE;NANDINAGARI LETTER KA;Lo;0;L;;;;;N;;;;;
+119AF;NANDINAGARI LETTER KHA;Lo;0;L;;;;;N;;;;;
+119B0;NANDINAGARI LETTER GA;Lo;0;L;;;;;N;;;;;
+119B1;NANDINAGARI LETTER GHA;Lo;0;L;;;;;N;;;;;
+119B2;NANDINAGARI LETTER NGA;Lo;0;L;;;;;N;;;;;
+119B3;NANDINAGARI LETTER CA;Lo;0;L;;;;;N;;;;;
+119B4;NANDINAGARI LETTER CHA;Lo;0;L;;;;;N;;;;;
+119B5;NANDINAGARI LETTER JA;Lo;0;L;;;;;N;;;;;
+119B6;NANDINAGARI LETTER JHA;Lo;0;L;;;;;N;;;;;
+119B7;NANDINAGARI LETTER NYA;Lo;0;L;;;;;N;;;;;
+119B8;NANDINAGARI LETTER TTA;Lo;0;L;;;;;N;;;;;
+119B9;NANDINAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+119BA;NANDINAGARI LETTER DDA;Lo;0;L;;;;;N;;;;;
+119BB;NANDINAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+119BC;NANDINAGARI LETTER NNA;Lo;0;L;;;;;N;;;;;
+119BD;NANDINAGARI LETTER TA;Lo;0;L;;;;;N;;;;;
+119BE;NANDINAGARI LETTER THA;Lo;0;L;;;;;N;;;;;
+119BF;NANDINAGARI LETTER DA;Lo;0;L;;;;;N;;;;;
+119C0;NANDINAGARI LETTER DHA;Lo;0;L;;;;;N;;;;;
+119C1;NANDINAGARI LETTER NA;Lo;0;L;;;;;N;;;;;
+119C2;NANDINAGARI LETTER PA;Lo;0;L;;;;;N;;;;;
+119C3;NANDINAGARI LETTER PHA;Lo;0;L;;;;;N;;;;;
+119C4;NANDINAGARI LETTER BA;Lo;0;L;;;;;N;;;;;
+119C5;NANDINAGARI LETTER BHA;Lo;0;L;;;;;N;;;;;
+119C6;NANDINAGARI LETTER MA;Lo;0;L;;;;;N;;;;;
+119C7;NANDINAGARI LETTER YA;Lo;0;L;;;;;N;;;;;
+119C8;NANDINAGARI LETTER RA;Lo;0;L;;;;;N;;;;;
+119C9;NANDINAGARI LETTER LA;Lo;0;L;;;;;N;;;;;
+119CA;NANDINAGARI LETTER VA;Lo;0;L;;;;;N;;;;;
+119CB;NANDINAGARI LETTER SHA;Lo;0;L;;;;;N;;;;;
+119CC;NANDINAGARI LETTER SSA;Lo;0;L;;;;;N;;;;;
+119CD;NANDINAGARI LETTER SA;Lo;0;L;;;;;N;;;;;
+119CE;NANDINAGARI LETTER HA;Lo;0;L;;;;;N;;;;;
+119CF;NANDINAGARI LETTER LLA;Lo;0;L;;;;;N;;;;;
+119D0;NANDINAGARI LETTER RRA;Lo;0;L;;;;;N;;;;;
+119D1;NANDINAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+119D2;NANDINAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+119D3;NANDINAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+119D4;NANDINAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+119D5;NANDINAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+119D6;NANDINAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+119D7;NANDINAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+119DA;NANDINAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+119DB;NANDINAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+119DC;NANDINAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+119DD;NANDINAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+119DE;NANDINAGARI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+119DF;NANDINAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+119E0;NANDINAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+119E1;NANDINAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+119E2;NANDINAGARI SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+119E3;NANDINAGARI HEADSTROKE;Lo;0;L;;;;;N;;;;;
+119E4;NANDINAGARI VOWEL SIGN PRISHTHAMATRA E;Mc;0;L;;;;;N;;;;;
11A00;ZANABAZAR SQUARE LETTER A;Lo;0;L;;;;;N;;;;;
11A01;ZANABAZAR SQUARE VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
11A02;ZANABAZAR SQUARE VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
@@ -20545,6 +20669,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11A81;SOYOMBO LETTER SA;Lo;0;L;;;;;N;;;;;
11A82;SOYOMBO LETTER HA;Lo;0;L;;;;;N;;;;;
11A83;SOYOMBO LETTER KSSA;Lo;0;L;;;;;N;;;;;
+11A84;SOYOMBO SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+11A85;SOYOMBO SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
11A86;SOYOMBO CLUSTER-INITIAL LETTER RA;Lo;0;L;;;;;N;;;;;
11A87;SOYOMBO CLUSTER-INITIAL LETTER LA;Lo;0;L;;;;;N;;;;;
11A88;SOYOMBO CLUSTER-INITIAL LETTER SHA;Lo;0;L;;;;;N;;;;;
@@ -20959,6 +21085,57 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11EF6;MAKASAR VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
11EF7;MAKASAR PASSIMBANG;Po;0;L;;;;;N;;;;;
11EF8;MAKASAR END OF SECTION;Po;0;L;;;;;N;;;;;
+11FC0;TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH;No;0;L;;;;1/320;N;;;;;
+11FC1;TAMIL FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;;
+11FC2;TAMIL FRACTION ONE EIGHTIETH;No;0;L;;;;1/80;N;;;;;
+11FC3;TAMIL FRACTION ONE SIXTY-FOURTH;No;0;L;;;;1/64;N;;;;;
+11FC4;TAMIL FRACTION ONE FORTIETH;No;0;L;;;;1/40;N;;;;;
+11FC5;TAMIL FRACTION ONE THIRTY-SECOND;No;0;L;;;;1/32;N;;;;;
+11FC6;TAMIL FRACTION THREE EIGHTIETHS;No;0;L;;;;3/80;N;;;;;
+11FC7;TAMIL FRACTION THREE SIXTY-FOURTHS;No;0;L;;;;3/64;N;;;;;
+11FC8;TAMIL FRACTION ONE TWENTIETH;No;0;L;;;;1/20;N;;;;;
+11FC9;TAMIL FRACTION ONE SIXTEENTH-1;No;0;L;;;;1/16;N;;;;;
+11FCA;TAMIL FRACTION ONE SIXTEENTH-2;No;0;L;;;;1/16;N;;;;;
+11FCB;TAMIL FRACTION ONE TENTH;No;0;L;;;;1/10;N;;;;;
+11FCC;TAMIL FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;;
+11FCD;TAMIL FRACTION THREE TWENTIETHS;No;0;L;;;;3/20;N;;;;;
+11FCE;TAMIL FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;;
+11FCF;TAMIL FRACTION ONE FIFTH;No;0;L;;;;1/5;N;;;;;
+11FD0;TAMIL FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;;
+11FD1;TAMIL FRACTION ONE HALF-1;No;0;L;;;;1/2;N;;;;;
+11FD2;TAMIL FRACTION ONE HALF-2;No;0;L;;;;1/2;N;;;;;
+11FD3;TAMIL FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;;
+11FD4;TAMIL FRACTION DOWNSCALING FACTOR KIIZH;No;0;L;;;;1/320;N;;;;;
+11FD5;TAMIL SIGN NEL;So;0;ON;;;;;N;;;;;
+11FD6;TAMIL SIGN CEVITU;So;0;ON;;;;;N;;;;;
+11FD7;TAMIL SIGN AAZHAAKKU;So;0;ON;;;;;N;;;;;
+11FD8;TAMIL SIGN UZHAKKU;So;0;ON;;;;;N;;;;;
+11FD9;TAMIL SIGN MUUVUZHAKKU;So;0;ON;;;;;N;;;;;
+11FDA;TAMIL SIGN KURUNI;So;0;ON;;;;;N;;;;;
+11FDB;TAMIL SIGN PATHAKKU;So;0;ON;;;;;N;;;;;
+11FDC;TAMIL SIGN MUKKURUNI;So;0;ON;;;;;N;;;;;
+11FDD;TAMIL SIGN KAACU;Sc;0;ET;;;;;N;;;;;
+11FDE;TAMIL SIGN PANAM;Sc;0;ET;;;;;N;;;;;
+11FDF;TAMIL SIGN PON;Sc;0;ET;;;;;N;;;;;
+11FE0;TAMIL SIGN VARAAKAN;Sc;0;ET;;;;;N;;;;;
+11FE1;TAMIL SIGN PAARAM;So;0;ON;;;;;N;;;;;
+11FE2;TAMIL SIGN KUZHI;So;0;ON;;;;;N;;;;;
+11FE3;TAMIL SIGN VELI;So;0;ON;;;;;N;;;;;
+11FE4;TAMIL WET CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE5;TAMIL DRY CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE6;TAMIL LAND SIGN;So;0;ON;;;;;N;;;;;
+11FE7;TAMIL SALT PAN SIGN;So;0;ON;;;;;N;;;;;
+11FE8;TAMIL TRADITIONAL CREDIT SIGN;So;0;ON;;;;;N;;;;;
+11FE9;TAMIL TRADITIONAL NUMBER SIGN;So;0;ON;;;;;N;;;;;
+11FEA;TAMIL CURRENT SIGN;So;0;ON;;;;;N;;;;;
+11FEB;TAMIL AND ODD SIGN;So;0;ON;;;;;N;;;;;
+11FEC;TAMIL SPENT SIGN;So;0;ON;;;;;N;;;;;
+11FED;TAMIL TOTAL SIGN;So;0;ON;;;;;N;;;;;
+11FEE;TAMIL IN POSSESSION SIGN;So;0;ON;;;;;N;;;;;
+11FEF;TAMIL STARTING FROM SIGN;So;0;ON;;;;;N;;;;;
+11FF0;TAMIL SIGN MUTHALIYA;So;0;ON;;;;;N;;;;;
+11FF1;TAMIL SIGN VAKAIYARAA;So;0;ON;;;;;N;;;;;
+11FFF;TAMIL PUNCTUATION END OF TEXT;Po;0;L;;;;;N;;;;;
12000;CUNEIFORM SIGN A;Lo;0;L;;;;;N;;;;;
12001;CUNEIFORM SIGN A TIMES A;Lo;0;L;;;;;N;;;;;
12002;CUNEIFORM SIGN A TIMES BAD;Lo;0;L;;;;;N;;;;;
@@ -23264,6 +23441,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1342C;EGYPTIAN HIEROGLYPH AA030;Lo;0;L;;;;;N;;;;;
1342D;EGYPTIAN HIEROGLYPH AA031;Lo;0;L;;;;;N;;;;;
1342E;EGYPTIAN HIEROGLYPH AA032;Lo;0;L;;;;;N;;;;;
+13430;EGYPTIAN HIEROGLYPH VERTICAL JOINER;Cf;0;L;;;;;N;;;;;
+13431;EGYPTIAN HIEROGLYPH HORIZONTAL JOINER;Cf;0;L;;;;;N;;;;;
+13432;EGYPTIAN HIEROGLYPH INSERT AT TOP START;Cf;0;L;;;;;N;;;;;
+13433;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM START;Cf;0;L;;;;;N;;;;;
+13434;EGYPTIAN HIEROGLYPH INSERT AT TOP END;Cf;0;L;;;;;N;;;;;
+13435;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM END;Cf;0;L;;;;;N;;;;;
+13436;EGYPTIAN HIEROGLYPH OVERLAY MIDDLE;Cf;0;L;;;;;N;;;;;
+13437;EGYPTIAN HIEROGLYPH BEGIN SEGMENT;Cf;0;L;;;;;N;;;;;
+13438;EGYPTIAN HIEROGLYPH END SEGMENT;Cf;0;L;;;;;N;;;;;
14400;ANATOLIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;;
14401;ANATOLIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;;
14402;ANATOLIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;;
@@ -24782,6 +24968,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F42;MIAO LETTER WA;Lo;0;L;;;;;N;;;;;
16F43;MIAO LETTER AH;Lo;0;L;;;;;N;;;;;
16F44;MIAO LETTER HHA;Lo;0;L;;;;;N;;;;;
+16F45;MIAO LETTER BRI;Lo;0;L;;;;;N;;;;;
+16F46;MIAO LETTER SYI;Lo;0;L;;;;;N;;;;;
+16F47;MIAO LETTER DZYI;Lo;0;L;;;;;N;;;;;
+16F48;MIAO LETTER TE;Lo;0;L;;;;;N;;;;;
+16F49;MIAO LETTER TSE;Lo;0;L;;;;;N;;;;;
+16F4A;MIAO LETTER RTE;Lo;0;L;;;;;N;;;;;
+16F4F;MIAO SIGN CONSONANT MODIFIER BAR;Mn;0;NSM;;;;;N;;;;;
16F50;MIAO LETTER NASALIZATION;Lo;0;L;;;;;N;;;;;
16F51;MIAO SIGN ASPIRATION;Mc;0;L;;;;;N;;;;;
16F52;MIAO SIGN REFORMED VOICING;Mc;0;L;;;;;N;;;;;
@@ -24829,6 +25022,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F7C;MIAO VOWEL SIGN OU;Mc;0;L;;;;;N;;;;;
16F7D;MIAO VOWEL SIGN N;Mc;0;L;;;;;N;;;;;
16F7E;MIAO VOWEL SIGN NG;Mc;0;L;;;;;N;;;;;
+16F7F;MIAO VOWEL SIGN UOG;Mc;0;L;;;;;N;;;;;
+16F80;MIAO VOWEL SIGN YUI;Mc;0;L;;;;;N;;;;;
+16F81;MIAO VOWEL SIGN OG;Mc;0;L;;;;;N;;;;;
+16F82;MIAO VOWEL SIGN OER;Mc;0;L;;;;;N;;;;;
+16F83;MIAO VOWEL SIGN VW;Mc;0;L;;;;;N;;;;;
+16F84;MIAO VOWEL SIGN IG;Mc;0;L;;;;;N;;;;;
+16F85;MIAO VOWEL SIGN EA;Mc;0;L;;;;;N;;;;;
+16F86;MIAO VOWEL SIGN IONG;Mc;0;L;;;;;N;;;;;
+16F87;MIAO VOWEL SIGN UI;Mc;0;L;;;;;N;;;;;
16F8F;MIAO TONE RIGHT;Mn;0;NSM;;;;;N;;;;;
16F90;MIAO TONE TOP RIGHT;Mn;0;NSM;;;;;N;;;;;
16F91;MIAO TONE ABOVE;Mn;0;NSM;;;;;N;;;;;
@@ -24848,8 +25050,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F9F;MIAO LETTER REFORMED TONE-8;Lm;0;L;;;;;N;;;;;
16FE0;TANGUT ITERATION MARK;Lm;0;L;;;;;N;;;;;
16FE1;NUSHU ITERATION MARK;Lm;0;L;;;;;N;;;;;
+16FE2;OLD CHINESE HOOK MARK;Po;0;ON;;;;;N;;;;;
+16FE3;OLD CHINESE ITERATION MARK;Lm;0;L;;;;;N;;;;;
17000;<Tangut Ideograph, First>;Lo;0;L;;;;;N;;;;;
-187F1;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+187F7;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;;
18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;;
18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;;
@@ -25892,6 +26096,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1B11C;HENTAIGANA LETTER WO-7;Lo;0;L;;;;;N;;;;;
1B11D;HENTAIGANA LETTER N-MU-MO-1;Lo;0;L;;;;;N;;;;;
1B11E;HENTAIGANA LETTER N-MU-MO-2;Lo;0;L;;;;;N;;;;;
+1B150;HIRAGANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B151;HIRAGANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B152;HIRAGANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B164;KATAKANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B165;KATAKANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B166;KATAKANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B167;KATAKANA LETTER SMALL N;Lo;0;L;;;;;N;;;;;
1B170;NUSHU CHARACTER-1B170;Lo;0;L;;;;;N;;;;;
1B171;NUSHU CHARACTER-1B171;Lo;0;L;;;;;N;;;;;
1B172;NUSHU CHARACTER-1B172;Lo;0;L;;;;;N;;;;;
@@ -28820,6 +29031,136 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E028;COMBINING GLAGOLITIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E029;COMBINING GLAGOLITIC LETTER IOTATED BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E02A;COMBINING GLAGOLITIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;
+1E100;NYIAKENG PUACHUE HMONG LETTER MA;Lo;0;L;;;;;N;;;;;
+1E101;NYIAKENG PUACHUE HMONG LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E102;NYIAKENG PUACHUE HMONG LETTER NTA;Lo;0;L;;;;;N;;;;;
+1E103;NYIAKENG PUACHUE HMONG LETTER TA;Lo;0;L;;;;;N;;;;;
+1E104;NYIAKENG PUACHUE HMONG LETTER HA;Lo;0;L;;;;;N;;;;;
+1E105;NYIAKENG PUACHUE HMONG LETTER NA;Lo;0;L;;;;;N;;;;;
+1E106;NYIAKENG PUACHUE HMONG LETTER XA;Lo;0;L;;;;;N;;;;;
+1E107;NYIAKENG PUACHUE HMONG LETTER NKA;Lo;0;L;;;;;N;;;;;
+1E108;NYIAKENG PUACHUE HMONG LETTER CA;Lo;0;L;;;;;N;;;;;
+1E109;NYIAKENG PUACHUE HMONG LETTER LA;Lo;0;L;;;;;N;;;;;
+1E10A;NYIAKENG PUACHUE HMONG LETTER SA;Lo;0;L;;;;;N;;;;;
+1E10B;NYIAKENG PUACHUE HMONG LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E10C;NYIAKENG PUACHUE HMONG LETTER NCA;Lo;0;L;;;;;N;;;;;
+1E10D;NYIAKENG PUACHUE HMONG LETTER NTSA;Lo;0;L;;;;;N;;;;;
+1E10E;NYIAKENG PUACHUE HMONG LETTER KA;Lo;0;L;;;;;N;;;;;
+1E10F;NYIAKENG PUACHUE HMONG LETTER DA;Lo;0;L;;;;;N;;;;;
+1E110;NYIAKENG PUACHUE HMONG LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E111;NYIAKENG PUACHUE HMONG LETTER NRA;Lo;0;L;;;;;N;;;;;
+1E112;NYIAKENG PUACHUE HMONG LETTER VA;Lo;0;L;;;;;N;;;;;
+1E113;NYIAKENG PUACHUE HMONG LETTER NTXA;Lo;0;L;;;;;N;;;;;
+1E114;NYIAKENG PUACHUE HMONG LETTER TXA;Lo;0;L;;;;;N;;;;;
+1E115;NYIAKENG PUACHUE HMONG LETTER FA;Lo;0;L;;;;;N;;;;;
+1E116;NYIAKENG PUACHUE HMONG LETTER RA;Lo;0;L;;;;;N;;;;;
+1E117;NYIAKENG PUACHUE HMONG LETTER QA;Lo;0;L;;;;;N;;;;;
+1E118;NYIAKENG PUACHUE HMONG LETTER YA;Lo;0;L;;;;;N;;;;;
+1E119;NYIAKENG PUACHUE HMONG LETTER NQA;Lo;0;L;;;;;N;;;;;
+1E11A;NYIAKENG PUACHUE HMONG LETTER PA;Lo;0;L;;;;;N;;;;;
+1E11B;NYIAKENG PUACHUE HMONG LETTER XYA;Lo;0;L;;;;;N;;;;;
+1E11C;NYIAKENG PUACHUE HMONG LETTER NPA;Lo;0;L;;;;;N;;;;;
+1E11D;NYIAKENG PUACHUE HMONG LETTER DLA;Lo;0;L;;;;;N;;;;;
+1E11E;NYIAKENG PUACHUE HMONG LETTER NPLA;Lo;0;L;;;;;N;;;;;
+1E11F;NYIAKENG PUACHUE HMONG LETTER HAH;Lo;0;L;;;;;N;;;;;
+1E120;NYIAKENG PUACHUE HMONG LETTER MLA;Lo;0;L;;;;;N;;;;;
+1E121;NYIAKENG PUACHUE HMONG LETTER PLA;Lo;0;L;;;;;N;;;;;
+1E122;NYIAKENG PUACHUE HMONG LETTER GA;Lo;0;L;;;;;N;;;;;
+1E123;NYIAKENG PUACHUE HMONG LETTER RRA;Lo;0;L;;;;;N;;;;;
+1E124;NYIAKENG PUACHUE HMONG LETTER A;Lo;0;L;;;;;N;;;;;
+1E125;NYIAKENG PUACHUE HMONG LETTER AA;Lo;0;L;;;;;N;;;;;
+1E126;NYIAKENG PUACHUE HMONG LETTER I;Lo;0;L;;;;;N;;;;;
+1E127;NYIAKENG PUACHUE HMONG LETTER U;Lo;0;L;;;;;N;;;;;
+1E128;NYIAKENG PUACHUE HMONG LETTER O;Lo;0;L;;;;;N;;;;;
+1E129;NYIAKENG PUACHUE HMONG LETTER OO;Lo;0;L;;;;;N;;;;;
+1E12A;NYIAKENG PUACHUE HMONG LETTER E;Lo;0;L;;;;;N;;;;;
+1E12B;NYIAKENG PUACHUE HMONG LETTER EE;Lo;0;L;;;;;N;;;;;
+1E12C;NYIAKENG PUACHUE HMONG LETTER W;Lo;0;L;;;;;N;;;;;
+1E130;NYIAKENG PUACHUE HMONG TONE-B;Mn;230;NSM;;;;;N;;;;;
+1E131;NYIAKENG PUACHUE HMONG TONE-M;Mn;230;NSM;;;;;N;;;;;
+1E132;NYIAKENG PUACHUE HMONG TONE-J;Mn;230;NSM;;;;;N;;;;;
+1E133;NYIAKENG PUACHUE HMONG TONE-V;Mn;230;NSM;;;;;N;;;;;
+1E134;NYIAKENG PUACHUE HMONG TONE-S;Mn;230;NSM;;;;;N;;;;;
+1E135;NYIAKENG PUACHUE HMONG TONE-G;Mn;230;NSM;;;;;N;;;;;
+1E136;NYIAKENG PUACHUE HMONG TONE-D;Mn;230;NSM;;;;;N;;;;;
+1E137;NYIAKENG PUACHUE HMONG SIGN FOR PERSON;Lm;0;L;;;;;N;;;;;
+1E138;NYIAKENG PUACHUE HMONG SIGN FOR THING;Lm;0;L;;;;;N;;;;;
+1E139;NYIAKENG PUACHUE HMONG SIGN FOR LOCATION;Lm;0;L;;;;;N;;;;;
+1E13A;NYIAKENG PUACHUE HMONG SIGN FOR ANIMAL;Lm;0;L;;;;;N;;;;;
+1E13B;NYIAKENG PUACHUE HMONG SIGN FOR INVERTEBRATE;Lm;0;L;;;;;N;;;;;
+1E13C;NYIAKENG PUACHUE HMONG SIGN XW XW;Lm;0;L;;;;;N;;;;;
+1E13D;NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER;Lm;0;L;;;;;N;;;;;
+1E140;NYIAKENG PUACHUE HMONG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E141;NYIAKENG PUACHUE HMONG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E142;NYIAKENG PUACHUE HMONG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E143;NYIAKENG PUACHUE HMONG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E144;NYIAKENG PUACHUE HMONG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E145;NYIAKENG PUACHUE HMONG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E146;NYIAKENG PUACHUE HMONG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E147;NYIAKENG PUACHUE HMONG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E148;NYIAKENG PUACHUE HMONG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E149;NYIAKENG PUACHUE HMONG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E14E;NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ;Lo;0;L;;;;;N;;;;;
+1E14F;NYIAKENG PUACHUE HMONG CIRCLED CA;So;0;L;;;;;N;;;;;
+1E2C0;WANCHO LETTER AA;Lo;0;L;;;;;N;;;;;
+1E2C1;WANCHO LETTER A;Lo;0;L;;;;;N;;;;;
+1E2C2;WANCHO LETTER BA;Lo;0;L;;;;;N;;;;;
+1E2C3;WANCHO LETTER CA;Lo;0;L;;;;;N;;;;;
+1E2C4;WANCHO LETTER DA;Lo;0;L;;;;;N;;;;;
+1E2C5;WANCHO LETTER GA;Lo;0;L;;;;;N;;;;;
+1E2C6;WANCHO LETTER YA;Lo;0;L;;;;;N;;;;;
+1E2C7;WANCHO LETTER PHA;Lo;0;L;;;;;N;;;;;
+1E2C8;WANCHO LETTER LA;Lo;0;L;;;;;N;;;;;
+1E2C9;WANCHO LETTER NA;Lo;0;L;;;;;N;;;;;
+1E2CA;WANCHO LETTER PA;Lo;0;L;;;;;N;;;;;
+1E2CB;WANCHO LETTER TA;Lo;0;L;;;;;N;;;;;
+1E2CC;WANCHO LETTER THA;Lo;0;L;;;;;N;;;;;
+1E2CD;WANCHO LETTER FA;Lo;0;L;;;;;N;;;;;
+1E2CE;WANCHO LETTER SA;Lo;0;L;;;;;N;;;;;
+1E2CF;WANCHO LETTER SHA;Lo;0;L;;;;;N;;;;;
+1E2D0;WANCHO LETTER JA;Lo;0;L;;;;;N;;;;;
+1E2D1;WANCHO LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E2D2;WANCHO LETTER WA;Lo;0;L;;;;;N;;;;;
+1E2D3;WANCHO LETTER VA;Lo;0;L;;;;;N;;;;;
+1E2D4;WANCHO LETTER KA;Lo;0;L;;;;;N;;;;;
+1E2D5;WANCHO LETTER O;Lo;0;L;;;;;N;;;;;
+1E2D6;WANCHO LETTER AU;Lo;0;L;;;;;N;;;;;
+1E2D7;WANCHO LETTER RA;Lo;0;L;;;;;N;;;;;
+1E2D8;WANCHO LETTER MA;Lo;0;L;;;;;N;;;;;
+1E2D9;WANCHO LETTER KHA;Lo;0;L;;;;;N;;;;;
+1E2DA;WANCHO LETTER HA;Lo;0;L;;;;;N;;;;;
+1E2DB;WANCHO LETTER E;Lo;0;L;;;;;N;;;;;
+1E2DC;WANCHO LETTER I;Lo;0;L;;;;;N;;;;;
+1E2DD;WANCHO LETTER NGA;Lo;0;L;;;;;N;;;;;
+1E2DE;WANCHO LETTER U;Lo;0;L;;;;;N;;;;;
+1E2DF;WANCHO LETTER LLHA;Lo;0;L;;;;;N;;;;;
+1E2E0;WANCHO LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E2E1;WANCHO LETTER TRA;Lo;0;L;;;;;N;;;;;
+1E2E2;WANCHO LETTER ONG;Lo;0;L;;;;;N;;;;;
+1E2E3;WANCHO LETTER AANG;Lo;0;L;;;;;N;;;;;
+1E2E4;WANCHO LETTER ANG;Lo;0;L;;;;;N;;;;;
+1E2E5;WANCHO LETTER ING;Lo;0;L;;;;;N;;;;;
+1E2E6;WANCHO LETTER ON;Lo;0;L;;;;;N;;;;;
+1E2E7;WANCHO LETTER EN;Lo;0;L;;;;;N;;;;;
+1E2E8;WANCHO LETTER AAN;Lo;0;L;;;;;N;;;;;
+1E2E9;WANCHO LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E2EA;WANCHO LETTER UEN;Lo;0;L;;;;;N;;;;;
+1E2EB;WANCHO LETTER YIH;Lo;0;L;;;;;N;;;;;
+1E2EC;WANCHO TONE TUP;Mn;230;NSM;;;;;N;;;;;
+1E2ED;WANCHO TONE TUPNI;Mn;230;NSM;;;;;N;;;;;
+1E2EE;WANCHO TONE KOI;Mn;230;NSM;;;;;N;;;;;
+1E2EF;WANCHO TONE KOINI;Mn;230;NSM;;;;;N;;;;;
+1E2F0;WANCHO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E2F1;WANCHO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E2F2;WANCHO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E2F3;WANCHO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E2F4;WANCHO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E2F5;WANCHO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E2F6;WANCHO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E2F7;WANCHO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E2F8;WANCHO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E2F9;WANCHO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E2FF;WANCHO NGUN SIGN;Sc;0;ET;;;;;N;;;;;
1E800;MENDE KIKAKUI SYLLABLE M001 KI;Lo;0;R;;;;;N;;;;;
1E801;MENDE KIKAKUI SYLLABLE M002 KA;Lo;0;R;;;;;N;;;;;
1E802;MENDE KIKAKUI SYLLABLE M003 KU;Lo;0;R;;;;;N;;;;;
@@ -29108,6 +29449,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E948;ADLAM CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
1E949;ADLAM GEMINATE CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
1E94A;ADLAM NUKTA;Mn;7;NSM;;;;;N;;;;;
+1E94B;ADLAM NASALIZATION MARK;Lm;0;R;;;;;N;;;;;
1E950;ADLAM DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;;
1E951;ADLAM DIGIT ONE;Nd;0;R;;1;1;1;N;;;;;
1E952;ADLAM DIGIT TWO;Nd;0;R;;2;2;2;N;;;;;
@@ -29188,6 +29530,67 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1ECB2;INDIC SIYAQ NUMBER ALTERNATE TWO;No;0;AL;;;;2;N;;;;;
1ECB3;INDIC SIYAQ NUMBER ALTERNATE TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
1ECB4;INDIC SIYAQ ALTERNATE LAKH MARK;No;0;AL;;;;100000;N;;;;;
+1ED01;OTTOMAN SIYAQ NUMBER ONE;No;0;AL;;;;1;N;;;;;
+1ED02;OTTOMAN SIYAQ NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED03;OTTOMAN SIYAQ NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED04;OTTOMAN SIYAQ NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED05;OTTOMAN SIYAQ NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED06;OTTOMAN SIYAQ NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED07;OTTOMAN SIYAQ NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED08;OTTOMAN SIYAQ NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED09;OTTOMAN SIYAQ NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED0A;OTTOMAN SIYAQ NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED0B;OTTOMAN SIYAQ NUMBER TWENTY;No;0;AL;;;;20;N;;;;;
+1ED0C;OTTOMAN SIYAQ NUMBER THIRTY;No;0;AL;;;;30;N;;;;;
+1ED0D;OTTOMAN SIYAQ NUMBER FORTY;No;0;AL;;;;40;N;;;;;
+1ED0E;OTTOMAN SIYAQ NUMBER FIFTY;No;0;AL;;;;50;N;;;;;
+1ED0F;OTTOMAN SIYAQ NUMBER SIXTY;No;0;AL;;;;60;N;;;;;
+1ED10;OTTOMAN SIYAQ NUMBER SEVENTY;No;0;AL;;;;70;N;;;;;
+1ED11;OTTOMAN SIYAQ NUMBER EIGHTY;No;0;AL;;;;80;N;;;;;
+1ED12;OTTOMAN SIYAQ NUMBER NINETY;No;0;AL;;;;90;N;;;;;
+1ED13;OTTOMAN SIYAQ NUMBER ONE HUNDRED;No;0;AL;;;;100;N;;;;;
+1ED14;OTTOMAN SIYAQ NUMBER TWO HUNDRED;No;0;AL;;;;200;N;;;;;
+1ED15;OTTOMAN SIYAQ NUMBER THREE HUNDRED;No;0;AL;;;;300;N;;;;;
+1ED16;OTTOMAN SIYAQ NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED17;OTTOMAN SIYAQ NUMBER FIVE HUNDRED;No;0;AL;;;;500;N;;;;;
+1ED18;OTTOMAN SIYAQ NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED19;OTTOMAN SIYAQ NUMBER SEVEN HUNDRED;No;0;AL;;;;700;N;;;;;
+1ED1A;OTTOMAN SIYAQ NUMBER EIGHT HUNDRED;No;0;AL;;;;800;N;;;;;
+1ED1B;OTTOMAN SIYAQ NUMBER NINE HUNDRED;No;0;AL;;;;900;N;;;;;
+1ED1C;OTTOMAN SIYAQ NUMBER ONE THOUSAND;No;0;AL;;;;1000;N;;;;;
+1ED1D;OTTOMAN SIYAQ NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED1E;OTTOMAN SIYAQ NUMBER THREE THOUSAND;No;0;AL;;;;3000;N;;;;;
+1ED1F;OTTOMAN SIYAQ NUMBER FOUR THOUSAND;No;0;AL;;;;4000;N;;;;;
+1ED20;OTTOMAN SIYAQ NUMBER FIVE THOUSAND;No;0;AL;;;;5000;N;;;;;
+1ED21;OTTOMAN SIYAQ NUMBER SIX THOUSAND;No;0;AL;;;;6000;N;;;;;
+1ED22;OTTOMAN SIYAQ NUMBER SEVEN THOUSAND;No;0;AL;;;;7000;N;;;;;
+1ED23;OTTOMAN SIYAQ NUMBER EIGHT THOUSAND;No;0;AL;;;;8000;N;;;;;
+1ED24;OTTOMAN SIYAQ NUMBER NINE THOUSAND;No;0;AL;;;;9000;N;;;;;
+1ED25;OTTOMAN SIYAQ NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED26;OTTOMAN SIYAQ NUMBER TWENTY THOUSAND;No;0;AL;;;;20000;N;;;;;
+1ED27;OTTOMAN SIYAQ NUMBER THIRTY THOUSAND;No;0;AL;;;;30000;N;;;;;
+1ED28;OTTOMAN SIYAQ NUMBER FORTY THOUSAND;No;0;AL;;;;40000;N;;;;;
+1ED29;OTTOMAN SIYAQ NUMBER FIFTY THOUSAND;No;0;AL;;;;50000;N;;;;;
+1ED2A;OTTOMAN SIYAQ NUMBER SIXTY THOUSAND;No;0;AL;;;;60000;N;;;;;
+1ED2B;OTTOMAN SIYAQ NUMBER SEVENTY THOUSAND;No;0;AL;;;;70000;N;;;;;
+1ED2C;OTTOMAN SIYAQ NUMBER EIGHTY THOUSAND;No;0;AL;;;;80000;N;;;;;
+1ED2D;OTTOMAN SIYAQ NUMBER NINETY THOUSAND;No;0;AL;;;;90000;N;;;;;
+1ED2E;OTTOMAN SIYAQ MARRATAN;So;0;AL;;;;;N;;;;;
+1ED2F;OTTOMAN SIYAQ ALTERNATE NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED30;OTTOMAN SIYAQ ALTERNATE NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED31;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED32;OTTOMAN SIYAQ ALTERNATE NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED33;OTTOMAN SIYAQ ALTERNATE NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED34;OTTOMAN SIYAQ ALTERNATE NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED35;OTTOMAN SIYAQ ALTERNATE NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED36;OTTOMAN SIYAQ ALTERNATE NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED37;OTTOMAN SIYAQ ALTERNATE NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED38;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED39;OTTOMAN SIYAQ ALTERNATE NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED3A;OTTOMAN SIYAQ ALTERNATE NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED3B;OTTOMAN SIYAQ ALTERNATE NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED3C;OTTOMAN SIYAQ FRACTION ONE HALF;No;0;AL;;;;1/2;N;;;;;
+1ED3D;OTTOMAN SIYAQ FRACTION ONE SIXTH;No;0;AL;;;;1/6;N;;;;;
1EE00;ARABIC MATHEMATICAL ALEF;Lo;0;AL;<font> 0627;;;;N;;;;;
1EE01;ARABIC MATHEMATICAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
1EE02;ARABIC MATHEMATICAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
@@ -29662,6 +30065,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F169;NEGATIVE CIRCLED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;;
1F16A;RAISED MC SIGN;So;0;ON;<super> 004D 0043;;;;N;;;;;
1F16B;RAISED MD SIGN;So;0;ON;<super> 004D 0044;;;;N;;;;;
+1F16C;RAISED MR SIGN;So;0;ON;<super> 004D 0052;;;;N;;;;;
1F170;NEGATIVE SQUARED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;;
1F171;NEGATIVE SQUARED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;;
1F172;NEGATIVE SQUARED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;;
@@ -30794,6 +31198,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6D2;SHOPPING TROLLEY;So;0;ON;;;;;N;;;;;
1F6D3;STUPA;So;0;ON;;;;;N;;;;;
1F6D4;PAGODA;So;0;ON;;;;;N;;;;;
+1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;;
1F6E0;HAMMER AND WRENCH;So;0;ON;;;;;N;;;;;
1F6E1;SHIELD;So;0;ON;;;;;N;;;;;
1F6E2;OIL DRUM;So;0;ON;;;;;N;;;;;
@@ -30817,6 +31222,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6F7;SLED;So;0;ON;;;;;N;;;;;
1F6F8;FLYING SAUCER;So;0;ON;;;;;N;;;;;
1F6F9;SKATEBOARD;So;0;ON;;;;;N;;;;;
+1F6FA;AUTO RICKSHAW;So;0;ON;;;;;N;;;;;
1F700;ALCHEMICAL SYMBOL FOR QUINTESSENCE;So;0;ON;;;;;N;;;;;
1F701;ALCHEMICAL SYMBOL FOR AIR;So;0;ON;;;;;N;;;;;
1F702;ALCHEMICAL SYMBOL FOR FIRE;So;0;ON;;;;;N;;;;;
@@ -31022,6 +31428,18 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F7D6;NEGATIVE CIRCLED TRIANGLE;So;0;ON;;;;;N;;;;;
1F7D7;CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
1F7D8;NEGATIVE CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E0;LARGE ORANGE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E1;LARGE YELLOW CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E2;LARGE GREEN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E3;LARGE PURPLE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E4;LARGE BROWN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E5;LARGE RED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E6;LARGE BLUE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E7;LARGE ORANGE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E8;LARGE YELLOW SQUARE;So;0;ON;;;;;N;;;;;
+1F7E9;LARGE GREEN SQUARE;So;0;ON;;;;;N;;;;;
+1F7EA;LARGE PURPLE SQUARE;So;0;ON;;;;;N;;;;;
+1F7EB;LARGE BROWN SQUARE;So;0;ON;;;;;N;;;;;
1F800;LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F801;UPWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F802;RIGHTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
@@ -31182,6 +31600,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F909;DOWNWARD FACING NOTCHED HOOK;So;0;ON;;;;;N;;;;;
1F90A;DOWNWARD FACING HOOK WITH DOT;So;0;ON;;;;;N;;;;;
1F90B;DOWNWARD FACING NOTCHED HOOK WITH DOT;So;0;ON;;;;;N;;;;;
+1F90D;WHITE HEART;So;0;ON;;;;;N;;;;;
+1F90E;BROWN HEART;So;0;ON;;;;;N;;;;;
+1F90F;PINCHING HAND;So;0;ON;;;;;N;;;;;
1F910;ZIPPER-MOUTH FACE;So;0;ON;;;;;N;;;;;
1F911;MONEY-MOUTH FACE;So;0;ON;;;;;N;;;;;
1F912;FACE WITH THERMOMETER;So;0;ON;;;;;N;;;;;
@@ -31229,6 +31650,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F93C;WRESTLERS;So;0;ON;;;;;N;;;;;
1F93D;WATER POLO;So;0;ON;;;;;N;;;;;
1F93E;HANDBALL;So;0;ON;;;;;N;;;;;
+1F93F;DIVING MASK;So;0;ON;;;;;N;;;;;
1F940;WILTED FLOWER;So;0;ON;;;;;N;;;;;
1F941;DRUM WITH DRUMSTICKS;So;0;ON;;;;;N;;;;;
1F942;CLINKING GLASSES;So;0;ON;;;;;N;;;;;
@@ -31278,11 +31700,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F96E;MOON CAKE;So;0;ON;;;;;N;;;;;
1F96F;BAGEL;So;0;ON;;;;;N;;;;;
1F970;SMILING FACE WITH SMILING EYES AND THREE HEARTS;So;0;ON;;;;;N;;;;;
+1F971;YAWNING FACE;So;0;ON;;;;;N;;;;;
1F973;FACE WITH PARTY HORN AND PARTY HAT;So;0;ON;;;;;N;;;;;
1F974;FACE WITH UNEVEN EYES AND WAVY MOUTH;So;0;ON;;;;;N;;;;;
1F975;OVERHEATED FACE;So;0;ON;;;;;N;;;;;
1F976;FREEZING FACE;So;0;ON;;;;;N;;;;;
1F97A;FACE WITH PLEADING EYES;So;0;ON;;;;;N;;;;;
+1F97B;SARI;So;0;ON;;;;;N;;;;;
1F97C;LAB COAT;So;0;ON;;;;;N;;;;;
1F97D;GOGGLES;So;0;ON;;;;;N;;;;;
1F97E;HIKING BOOT;So;0;ON;;;;;N;;;;;
@@ -31322,6 +31746,14 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9A0;MICROBE;So;0;ON;;;;;N;;;;;
1F9A1;BADGER;So;0;ON;;;;;N;;;;;
1F9A2;SWAN;So;0;ON;;;;;N;;;;;
+1F9A5;SLOTH;So;0;ON;;;;;N;;;;;
+1F9A6;OTTER;So;0;ON;;;;;N;;;;;
+1F9A7;ORANGUTAN;So;0;ON;;;;;N;;;;;
+1F9A8;SKUNK;So;0;ON;;;;;N;;;;;
+1F9A9;FLAMINGO;So;0;ON;;;;;N;;;;;
+1F9AA;OYSTER;So;0;ON;;;;;N;;;;;
+1F9AE;GUIDE DOG;So;0;ON;;;;;N;;;;;
+1F9AF;PROBING CANE;So;0;ON;;;;;N;;;;;
1F9B0;EMOJI COMPONENT RED HAIR;So;0;ON;;;;;N;;;;;
1F9B1;EMOJI COMPONENT CURLY HAIR;So;0;ON;;;;;N;;;;;
1F9B2;EMOJI COMPONENT BALD;So;0;ON;;;;;N;;;;;
@@ -31332,9 +31764,26 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9B7;TOOTH;So;0;ON;;;;;N;;;;;
1F9B8;SUPERHERO;So;0;ON;;;;;N;;;;;
1F9B9;SUPERVILLAIN;So;0;ON;;;;;N;;;;;
+1F9BA;SAFETY VEST;So;0;ON;;;;;N;;;;;
+1F9BB;EAR WITH HEARING AID;So;0;ON;;;;;N;;;;;
+1F9BC;MOTORIZED WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BD;MANUAL WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BE;MECHANICAL ARM;So;0;ON;;;;;N;;;;;
+1F9BF;MECHANICAL LEG;So;0;ON;;;;;N;;;;;
1F9C0;CHEESE WEDGE;So;0;ON;;;;;N;;;;;
1F9C1;CUPCAKE;So;0;ON;;;;;N;;;;;
1F9C2;SALT SHAKER;So;0;ON;;;;;N;;;;;
+1F9C3;BEVERAGE BOX;So;0;ON;;;;;N;;;;;
+1F9C4;GARLIC;So;0;ON;;;;;N;;;;;
+1F9C5;ONION;So;0;ON;;;;;N;;;;;
+1F9C6;FALAFEL;So;0;ON;;;;;N;;;;;
+1F9C7;WAFFLE;So;0;ON;;;;;N;;;;;
+1F9C8;BUTTER;So;0;ON;;;;;N;;;;;
+1F9C9;MATE DRINK;So;0;ON;;;;;N;;;;;
+1F9CA;ICE CUBE;So;0;ON;;;;;N;;;;;
+1F9CD;STANDING PERSON;So;0;ON;;;;;N;;;;;
+1F9CE;KNEELING PERSON;So;0;ON;;;;;N;;;;;
+1F9CF;DEAF PERSON;So;0;ON;;;;;N;;;;;
1F9D0;FACE WITH MONOCLE;So;0;ON;;;;;N;;;;;
1F9D1;ADULT;So;0;ON;;;;;N;;;;;
1F9D2;CHILD;So;0;ON;;;;;N;;;;;
@@ -31383,6 +31832,90 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9FD;SPONGE;So;0;ON;;;;;N;;;;;
1F9FE;RECEIPT;So;0;ON;;;;;N;;;;;
1F9FF;NAZAR AMULET;So;0;ON;;;;;N;;;;;
+1FA00;NEUTRAL CHESS KING;So;0;ON;;;;;N;;;;;
+1FA01;NEUTRAL CHESS QUEEN;So;0;ON;;;;;N;;;;;
+1FA02;NEUTRAL CHESS ROOK;So;0;ON;;;;;N;;;;;
+1FA03;NEUTRAL CHESS BISHOP;So;0;ON;;;;;N;;;;;
+1FA04;NEUTRAL CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+1FA05;NEUTRAL CHESS PAWN;So;0;ON;;;;;N;;;;;
+1FA06;WHITE CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA07;BLACK CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA08;NEUTRAL CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA09;WHITE CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0A;WHITE CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0B;WHITE CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0C;WHITE CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0D;WHITE CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0E;WHITE CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0F;BLACK CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA10;BLACK CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA11;BLACK CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA12;BLACK CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA13;BLACK CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA14;BLACK CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA15;NEUTRAL CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA16;NEUTRAL CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA17;NEUTRAL CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA18;NEUTRAL CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA19;NEUTRAL CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1A;NEUTRAL CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1B;WHITE CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1C;BLACK CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1D;NEUTRAL CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1E;WHITE CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA1F;WHITE CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA20;WHITE CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA21;WHITE CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA22;WHITE CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA23;WHITE CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA24;BLACK CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA25;BLACK CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA26;BLACK CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA27;BLACK CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA28;BLACK CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA29;BLACK CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA2A;NEUTRAL CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA2B;NEUTRAL CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA2C;NEUTRAL CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA2D;NEUTRAL CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA2E;NEUTRAL CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA2F;NEUTRAL CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA30;WHITE CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA31;BLACK CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA32;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA33;WHITE CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA34;WHITE CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA35;WHITE CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA36;WHITE CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA37;WHITE CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA38;WHITE CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA39;BLACK CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3A;BLACK CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3B;BLACK CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3C;BLACK CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3D;BLACK CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3E;BLACK CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3F;NEUTRAL CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA40;NEUTRAL CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA41;NEUTRAL CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA42;NEUTRAL CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA43;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA44;NEUTRAL CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA45;WHITE CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA46;BLACK CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA47;NEUTRAL CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA48;WHITE CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA49;BLACK CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4A;NEUTRAL CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4B;WHITE CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4C;BLACK CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4D;NEUTRAL CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4E;WHITE CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA4F;WHITE CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA50;WHITE CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
+1FA51;BLACK CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA52;BLACK CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA53;BLACK CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
1FA60;XIANGQI RED GENERAL;So;0;ON;;;;;N;;;;;
1FA61;XIANGQI RED MANDARIN;So;0;ON;;;;;N;;;;;
1FA62;XIANGQI RED ELEPHANT;So;0;ON;;;;;N;;;;;
@@ -31397,6 +31930,22 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FA6B;XIANGQI BLACK CHARIOT;So;0;ON;;;;;N;;;;;
1FA6C;XIANGQI BLACK CANNON;So;0;ON;;;;;N;;;;;
1FA6D;XIANGQI BLACK SOLDIER;So;0;ON;;;;;N;;;;;
+1FA70;BALLET SHOES;So;0;ON;;;;;N;;;;;
+1FA71;ONE-PIECE SWIMSUIT;So;0;ON;;;;;N;;;;;
+1FA72;BRIEFS;So;0;ON;;;;;N;;;;;
+1FA73;SHORTS;So;0;ON;;;;;N;;;;;
+1FA78;DROP OF BLOOD;So;0;ON;;;;;N;;;;;
+1FA79;ADHESIVE BANDAGE;So;0;ON;;;;;N;;;;;
+1FA7A;STETHOSCOPE;So;0;ON;;;;;N;;;;;
+1FA80;YO-YO;So;0;ON;;;;;N;;;;;
+1FA81;KITE;So;0;ON;;;;;N;;;;;
+1FA82;PARACHUTE;So;0;ON;;;;;N;;;;;
+1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;;
+1FA91;CHAIR;So;0;ON;;;;;N;;;;;
+1FA92;RAZOR;So;0;ON;;;;;N;;;;;
+1FA93;AXE;So;0;ON;;;;;N;;;;;
+1FA94;DIYA LAMP;So;0;ON;;;;;N;;;;;
+1FA95;BANJO;So;0;ON;;;;;N;;;;;
20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;;
diff --git a/lib/stdlib/uc_spec/emoji-data.txt b/lib/stdlib/uc_spec/emoji-data.txt
index 6e66455252..2fb5c3ff68 100644
--- a/lib/stdlib/uc_spec/emoji-data.txt
+++ b/lib/stdlib/uc_spec/emoji-data.txt
@@ -1,11 +1,11 @@
# emoji-data.txt
-# Date: 2018-02-07, 07:55:18 GMT
-# © 2018 Unicode®, Inc.
+# Date: 2019-01-15, 12:10:05 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Emoji Data for UTS #51
-# Version: 11.0
+# Version: 12.0
#
# For documentation and usage, see http://www.unicode.org/reports/tr51
#
@@ -45,7 +45,7 @@
25FB..25FE ; Emoji # 3.2 [4] (◻️..◾) white medium square..black medium-small square
2600..2604 ; Emoji # 1.1 [5] (☀️..☄️) sun..comet
260E ; Emoji # 1.1 [1] (☎️) telephone
-2611 ; Emoji # 1.1 [1] (☑️) ballot box with check
+2611 ; Emoji # 1.1 [1] (☑️) check box with check
2614..2615 ; Emoji # 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage
2618 ; Emoji # 4.1 [1] (☘️) shamrock
261D ; Emoji # 1.1 [1] (☝️) index pointing up
@@ -82,14 +82,14 @@
26F7..26FA ; Emoji # 5.2 [4] (⛷️..⛺) skier..tent
26FD ; Emoji # 5.2 [1] (⛽) fuel pump
2702 ; Emoji # 1.1 [1] (✂️) scissors
-2705 ; Emoji # 6.0 [1] (✅) white heavy check mark
+2705 ; Emoji # 6.0 [1] (✅) check mark button
2708..2709 ; Emoji # 1.1 [2] (✈️..✉️) airplane..envelope
270A..270B ; Emoji # 6.0 [2] (✊..✋) raised fist..raised hand
270C..270D ; Emoji # 1.1 [2] (✌️..✍️) victory hand..writing hand
270F ; Emoji # 1.1 [1] (✏️) pencil
2712 ; Emoji # 1.1 [1] (✒️) black nib
-2714 ; Emoji # 1.1 [1] (✔️) heavy check mark
-2716 ; Emoji # 1.1 [1] (✖️) heavy multiplication x
+2714 ; Emoji # 1.1 [1] (✔️) check mark
+2716 ; Emoji # 1.1 [1] (✖️) multiplication sign
271D ; Emoji # 1.1 [1] (✝️) latin cross
2721 ; Emoji # 1.1 [1] (✡️) star of David
2728 ; Emoji # 6.0 [1] (✨) sparkles
@@ -100,8 +100,8 @@
274E ; Emoji # 6.0 [1] (❎) cross mark button
2753..2755 ; Emoji # 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Emoji # 5.2 [1] (❗) exclamation mark
-2763..2764 ; Emoji # 1.1 [2] (❣️..❤️) heavy heart exclamation..red heart
-2795..2797 ; Emoji # 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2763..2764 ; Emoji # 1.1 [2] (❣️..❤️) heart exclamation..red heart
+2795..2797 ; Emoji # 6.0 [3] (➕..➗) plus sign..division sign
27A1 ; Emoji # 1.1 [1] (➡️) right arrow
27B0 ; Emoji # 6.0 [1] (➰) curly loop
27BF ; Emoji # 6.0 [1] (➿) double curly loop
@@ -109,7 +109,7 @@
2B05..2B07 ; Emoji # 4.0 [3] (⬅️..⬇️) left arrow..down arrow
2B1B..2B1C ; Emoji # 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Emoji # 5.1 [1] (⭐) star
-2B55 ; Emoji # 5.2 [1] (⭕) heavy large circle
+2B55 ; Emoji # 5.2 [1] (⭕) hollow red circle
3030 ; Emoji # 1.1 [1] (〰️) wavy dash
303D ; Emoji # 3.2 [1] (〽️) part alternation mark
3297 ; Emoji # 1.1 [1] (㊗️) Japanese “congratulations” button
@@ -206,7 +206,7 @@
1F62E..1F62F ; Emoji # 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji # 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji # 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Emoji # 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Emoji # 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Emoji # 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Emoji # 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Emoji # 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
@@ -214,6 +214,7 @@
1F6CB..1F6CF ; Emoji # 7.0 [5] (🛋️..🛏️) couch and lamp..bed
1F6D0 ; Emoji # 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Emoji # 9.0 [2] (🛑..🛒) stop sign..shopping cart
+1F6D5 ; Emoji # 12.0 [1] (🛕) hindu temple
1F6E0..1F6E5 ; Emoji # 7.0 [6] (🛠️..🛥️) hammer and wrench..motor boat
1F6E9 ; Emoji # 7.0 [1] (🛩️) small airplane
1F6EB..1F6EC ; Emoji # 7.0 [2] (🛫..🛬) airplane departure..airplane arrival
@@ -222,6 +223,9 @@
1F6F4..1F6F6 ; Emoji # 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Emoji # 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Emoji # 11.0 [1] (🛹) skateboard
+1F6FA ; Emoji # 12.0 [1] (🛺) auto rickshaw
+1F7E0..1F7EB ; Emoji # 12.0 [12] (🟠..🟫) orange circle..brown square
+1F90D..1F90F ; Emoji # 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji # 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Emoji # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji # 10.0 [1] (🤟) love-you gesture
@@ -231,27 +235,39 @@
1F931..1F932 ; Emoji # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Emoji # 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Emoji # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
+1F93F ; Emoji # 12.0 [1] (🤿) diving mask
1F940..1F945 ; Emoji # 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Emoji # 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Emoji # 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Emoji # 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Emoji # 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Emoji # 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Emoji # 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
+1F96C..1F970 ; Emoji # 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Emoji # 12.0 [1] (🥱) yawning face
1F973..1F976 ; Emoji # 11.0 [4] (🥳..🥶) partying face..cold face
1F97A ; Emoji # 11.0 [1] (🥺) pleading face
-1F97C..1F97F ; Emoji # 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Emoji # 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Emoji # 12.0 [1] (🥻) sari
+1F97C..1F97F ; Emoji # 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Emoji # 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Emoji # 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Emoji # 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Emoji # 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9B0..1F9B9 ; Emoji # 11.0 [10] (🦰..🦹) red-haired..supervillain
+1F9A5..1F9AA ; Emoji # 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AE..1F9AF ; Emoji # 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji # 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Emoji # 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Emoji # 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Emoji # 11.0 [2] (🧁..🧂) cupcake..salt
+1F9C3..1F9CA ; Emoji # 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CD..1F9CF ; Emoji # 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji # 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji # 11.0 [25] (🧧..🧿) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji # 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA78..1FA7A ; Emoji # 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji # 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA90..1FA95 ; Emoji # 12.0 [6] (🪐..🪕) ringed planet..banjo
-# Total elements: 1250
+# Total elements: 1311
# ================================================
@@ -278,19 +294,19 @@
26F5 ; Emoji_Presentation # 5.2 [1] (⛵) sailboat
26FA ; Emoji_Presentation # 5.2 [1] (⛺) tent
26FD ; Emoji_Presentation # 5.2 [1] (⛽) fuel pump
-2705 ; Emoji_Presentation # 6.0 [1] (✅) white heavy check mark
+2705 ; Emoji_Presentation # 6.0 [1] (✅) check mark button
270A..270B ; Emoji_Presentation # 6.0 [2] (✊..✋) raised fist..raised hand
2728 ; Emoji_Presentation # 6.0 [1] (✨) sparkles
274C ; Emoji_Presentation # 6.0 [1] (❌) cross mark
274E ; Emoji_Presentation # 6.0 [1] (❎) cross mark button
2753..2755 ; Emoji_Presentation # 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Emoji_Presentation # 5.2 [1] (❗) exclamation mark
-2795..2797 ; Emoji_Presentation # 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2795..2797 ; Emoji_Presentation # 6.0 [3] (➕..➗) plus sign..division sign
27B0 ; Emoji_Presentation # 6.0 [1] (➰) curly loop
27BF ; Emoji_Presentation # 6.0 [1] (➿) double curly loop
2B1B..2B1C ; Emoji_Presentation # 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Emoji_Presentation # 5.1 [1] (⭐) star
-2B55 ; Emoji_Presentation # 5.2 [1] (⭕) heavy large circle
+2B55 ; Emoji_Presentation # 5.2 [1] (⭕) hollow red circle
1F004 ; Emoji_Presentation # 5.1 [1] (🀄) mahjong red dragon
1F0CF ; Emoji_Presentation # 6.0 [1] (🃏) joker
1F18E ; Emoji_Presentation # 6.0 [1] (🆎) AB button (blood type)
@@ -349,7 +365,7 @@
1F62E..1F62F ; Emoji_Presentation # 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji_Presentation # 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji_Presentation # 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Emoji_Presentation # 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Emoji_Presentation # 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Emoji_Presentation # 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Emoji_Presentation # 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Emoji_Presentation # 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
@@ -357,10 +373,14 @@
1F6CC ; Emoji_Presentation # 7.0 [1] (🛌) person in bed
1F6D0 ; Emoji_Presentation # 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Emoji_Presentation # 9.0 [2] (🛑..🛒) stop sign..shopping cart
+1F6D5 ; Emoji_Presentation # 12.0 [1] (🛕) hindu temple
1F6EB..1F6EC ; Emoji_Presentation # 7.0 [2] (🛫..🛬) airplane departure..airplane arrival
1F6F4..1F6F6 ; Emoji_Presentation # 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Emoji_Presentation # 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Emoji_Presentation # 11.0 [1] (🛹) skateboard
+1F6FA ; Emoji_Presentation # 12.0 [1] (🛺) auto rickshaw
+1F7E0..1F7EB ; Emoji_Presentation # 12.0 [12] (🟠..🟫) orange circle..brown square
+1F90D..1F90F ; Emoji_Presentation # 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji_Presentation # 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Emoji_Presentation # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji_Presentation # 10.0 [1] (🤟) love-you gesture
@@ -370,27 +390,39 @@
1F931..1F932 ; Emoji_Presentation # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Emoji_Presentation # 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Emoji_Presentation # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
+1F93F ; Emoji_Presentation # 12.0 [1] (🤿) diving mask
1F940..1F945 ; Emoji_Presentation # 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Emoji_Presentation # 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Emoji_Presentation # 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Emoji_Presentation # 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Emoji_Presentation # 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Emoji_Presentation # 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Emoji_Presentation # 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
+1F96C..1F970 ; Emoji_Presentation # 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Emoji_Presentation # 12.0 [1] (🥱) yawning face
1F973..1F976 ; Emoji_Presentation # 11.0 [4] (🥳..🥶) partying face..cold face
1F97A ; Emoji_Presentation # 11.0 [1] (🥺) pleading face
-1F97C..1F97F ; Emoji_Presentation # 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Emoji_Presentation # 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Emoji_Presentation # 12.0 [1] (🥻) sari
+1F97C..1F97F ; Emoji_Presentation # 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Emoji_Presentation # 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Emoji_Presentation # 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Emoji_Presentation # 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Emoji_Presentation # 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9B0..1F9B9 ; Emoji_Presentation # 11.0 [10] (🦰..🦹) red-haired..supervillain
+1F9A5..1F9AA ; Emoji_Presentation # 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AE..1F9AF ; Emoji_Presentation # 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji_Presentation # 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Emoji_Presentation # 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Emoji_Presentation # 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Emoji_Presentation # 11.0 [2] (🧁..🧂) cupcake..salt
+1F9C3..1F9CA ; Emoji_Presentation # 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CD..1F9CF ; Emoji_Presentation # 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji_Presentation # 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji_Presentation # 11.0 [25] (🧧..🧿) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji_Presentation # 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA78..1FA7A ; Emoji_Presentation # 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji_Presentation # 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA90..1FA95 ; Emoji_Presentation # 12.0 [6] (🪐..🪕) ringed planet..banjo
-# Total elements: 1032
+# Total elements: 1093
# ================================================
@@ -417,12 +449,12 @@
1F3CB..1F3CC ; Emoji_Modifier_Base # 7.0 [2] (🏋️..🏌️) person lifting weights..person golfing
1F442..1F443 ; Emoji_Modifier_Base # 6.0 [2] (👂..👃) ear..nose
1F446..1F450 ; Emoji_Modifier_Base # 6.0 [11] (👆..👐) backhand index pointing up..open hands
-1F466..1F469 ; Emoji_Modifier_Base # 6.0 [4] (👦..👩) boy..woman
-1F46E ; Emoji_Modifier_Base # 6.0 [1] (👮) police officer
-1F470..1F478 ; Emoji_Modifier_Base # 6.0 [9] (👰..👸) bride with veil..princess
+1F466..1F478 ; Emoji_Modifier_Base # 6.0 [19] (👦..👸) boy..princess
1F47C ; Emoji_Modifier_Base # 6.0 [1] (👼) baby angel
1F481..1F483 ; Emoji_Modifier_Base # 6.0 [3] (💁..💃) person tipping hand..woman dancing
1F485..1F487 ; Emoji_Modifier_Base # 6.0 [3] (💅..💇) nail polish..person getting haircut
+1F48F ; Emoji_Modifier_Base # 6.0 [1] (💏) kiss
+1F491 ; Emoji_Modifier_Base # 6.0 [1] (💑) couple with heart
1F4AA ; Emoji_Modifier_Base # 6.0 [1] (💪) flexed biceps
1F574..1F575 ; Emoji_Modifier_Base # 7.0 [2] (🕴️..🕵️) man in suit levitating..detective
1F57A ; Emoji_Modifier_Base # 9.0 [1] (🕺) man dancing
@@ -434,20 +466,22 @@
1F6B4..1F6B6 ; Emoji_Modifier_Base # 6.0 [3] (🚴..🚶) person biking..person walking
1F6C0 ; Emoji_Modifier_Base # 6.0 [1] (🛀) person taking bath
1F6CC ; Emoji_Modifier_Base # 7.0 [1] (🛌) person in bed
+1F90F ; Emoji_Modifier_Base # 12.0 [1] (🤏) pinching hand
1F918 ; Emoji_Modifier_Base # 8.0 [1] (🤘) sign of the horns
-1F919..1F91C ; Emoji_Modifier_Base # 9.0 [4] (🤙..🤜) call me hand..right-facing fist
-1F91E ; Emoji_Modifier_Base # 9.0 [1] (🤞) crossed fingers
+1F919..1F91E ; Emoji_Modifier_Base # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji_Modifier_Base # 10.0 [1] (🤟) love-you gesture
1F926 ; Emoji_Modifier_Base # 9.0 [1] (🤦) person facepalming
1F930 ; Emoji_Modifier_Base # 9.0 [1] (🤰) pregnant woman
1F931..1F932 ; Emoji_Modifier_Base # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F939 ; Emoji_Modifier_Base # 9.0 [7] (🤳..🤹) selfie..person juggling
-1F93D..1F93E ; Emoji_Modifier_Base # 9.0 [2] (🤽..🤾) person playing water polo..person playing handball
+1F93C..1F93E ; Emoji_Modifier_Base # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
1F9B5..1F9B6 ; Emoji_Modifier_Base # 11.0 [2] (🦵..🦶) leg..foot
1F9B8..1F9B9 ; Emoji_Modifier_Base # 11.0 [2] (🦸..🦹) superhero..supervillain
-1F9D1..1F9DD ; Emoji_Modifier_Base # 10.0 [13] (🧑..🧝) adult..elf
+1F9BB ; Emoji_Modifier_Base # 12.0 [1] (🦻) ear with hearing aid
+1F9CD..1F9CF ; Emoji_Modifier_Base # 12.0 [3] (🧍..🧏) person standing..deaf person
+1F9D1..1F9DD ; Emoji_Modifier_Base # 10.0 [13] (🧑..🧝) person..elf
-# Total elements: 106
+# Total elements: 120
# ================================================
@@ -462,7 +496,7 @@
FE0F ; Emoji_Component # 3.2 [1] () VARIATION SELECTOR-16
1F1E6..1F1FF ; Emoji_Component # 6.0 [26] (🇦..🇿) regional indicator symbol letter a..regional indicator symbol letter z
1F3FB..1F3FF ; Emoji_Component # 8.0 [5] (🏻..🏿) light skin tone..dark skin tone
-1F9B0..1F9B3 ; Emoji_Component # 11.0 [4] (🦰..🦳) red-haired..white-haired
+1F9B0..1F9B3 ; Emoji_Component # 11.0 [4] (🦰..🦳) red hair..white hair
E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..cancel tag
# Total elements: 146
@@ -482,7 +516,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
21A9..21AA ; Extended_Pictographic# 1.1 [2] (↩️..↪️) right arrow curving left..left arrow curving right
231A..231B ; Extended_Pictographic# 1.1 [2] (⌚..⌛) watch..hourglass done
2328 ; Extended_Pictographic# 1.1 [1] (⌨️) keyboard
-2388 ; Extended_Pictographic# 3.0 [1] (⎈️) HELM SYMBOL
+2388 ; Extended_Pictographic# 3.0 [1] (⎈) HELM SYMBOL
23CF ; Extended_Pictographic# 4.0 [1] (⏏️) eject button
23E9..23F3 ; Extended_Pictographic# 6.0 [11] (⏩..⏳) fast-forward button..hourglass not done
23F8..23FA ; Extended_Pictographic# 7.0 [3] (⏸️..⏺️) pause button..record button
@@ -491,42 +525,42 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
25B6 ; Extended_Pictographic# 1.1 [1] (▶️) play button
25C0 ; Extended_Pictographic# 1.1 [1] (◀️) reverse button
25FB..25FE ; Extended_Pictographic# 3.2 [4] (◻️..◾) white medium square..black medium-small square
-2600..2605 ; Extended_Pictographic# 1.1 [6] (☀️..★️) sun..BLACK STAR
-2607..2612 ; Extended_Pictographic# 1.1 [12] (☇️..☒️) LIGHTNING..BALLOT BOX WITH X
+2600..2605 ; Extended_Pictographic# 1.1 [6] (☀️..★) sun..BLACK STAR
+2607..2612 ; Extended_Pictographic# 1.1 [12] (☇..☒) LIGHTNING..BALLOT BOX WITH X
2614..2615 ; Extended_Pictographic# 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage
-2616..2617 ; Extended_Pictographic# 3.2 [2] (☖️..☗️) WHITE SHOGI PIECE..BLACK SHOGI PIECE
+2616..2617 ; Extended_Pictographic# 3.2 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE
2618 ; Extended_Pictographic# 4.1 [1] (☘️) shamrock
-2619 ; Extended_Pictographic# 3.0 [1] (☙️) REVERSED ROTATED FLORAL HEART BULLET
-261A..266F ; Extended_Pictographic# 1.1 [86] (☚️..♯️) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
-2670..2671 ; Extended_Pictographic# 3.0 [2] (♰️..♱️) WEST SYRIAC CROSS..EAST SYRIAC CROSS
-2672..267D ; Extended_Pictographic# 3.2 [12] (♲️..♽️) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
+2619 ; Extended_Pictographic# 3.0 [1] (☙) REVERSED ROTATED FLORAL HEART BULLET
+261A..266F ; Extended_Pictographic# 1.1 [86] (☚..♯) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
+2670..2671 ; Extended_Pictographic# 3.0 [2] (♰..♱) WEST SYRIAC CROSS..EAST SYRIAC CROSS
+2672..267D ; Extended_Pictographic# 3.2 [12] (♲..♽) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
267E..267F ; Extended_Pictographic# 4.1 [2] (♾️..♿) infinity..wheelchair symbol
-2680..2685 ; Extended_Pictographic# 3.2 [6] (⚀️..⚅️) DIE FACE-1..DIE FACE-6
-2690..2691 ; Extended_Pictographic# 4.0 [2] (⚐️..⚑️) WHITE FLAG..BLACK FLAG
+2680..2685 ; Extended_Pictographic# 3.2 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6
+2690..2691 ; Extended_Pictographic# 4.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG
2692..269C ; Extended_Pictographic# 4.1 [11] (⚒️..⚜️) hammer and pick..fleur-de-lis
-269D ; Extended_Pictographic# 5.1 [1] (⚝️) OUTLINED WHITE STAR
-269E..269F ; Extended_Pictographic# 5.2 [2] (⚞️..⚟️) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
+269D ; Extended_Pictographic# 5.1 [1] (⚝) OUTLINED WHITE STAR
+269E..269F ; Extended_Pictographic# 5.2 [2] (⚞..⚟) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
26A0..26A1 ; Extended_Pictographic# 4.0 [2] (⚠️..⚡) warning..high voltage
-26A2..26B1 ; Extended_Pictographic# 4.1 [16] (⚢️..⚱️) DOUBLED FEMALE SIGN..funeral urn
-26B2 ; Extended_Pictographic# 5.0 [1] (⚲️) NEUTER
-26B3..26BC ; Extended_Pictographic# 5.1 [10] (⚳️..⚼️) CERES..SESQUIQUADRATE
-26BD..26BF ; Extended_Pictographic# 5.2 [3] (⚽..⚿️) soccer ball..SQUARED KEY
-26C0..26C3 ; Extended_Pictographic# 5.1 [4] (⛀️..⛃️) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
-26C4..26CD ; Extended_Pictographic# 5.2 [10] (⛄..⛍️) snowman without snow..DISABLED CAR
+26A2..26B1 ; Extended_Pictographic# 4.1 [16] (⚢..⚱️) DOUBLED FEMALE SIGN..funeral urn
+26B2 ; Extended_Pictographic# 5.0 [1] (⚲) NEUTER
+26B3..26BC ; Extended_Pictographic# 5.1 [10] (⚳..⚼) CERES..SESQUIQUADRATE
+26BD..26BF ; Extended_Pictographic# 5.2 [3] (⚽..⚿) soccer ball..SQUARED KEY
+26C0..26C3 ; Extended_Pictographic# 5.1 [4] (⛀..⛃) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+26C4..26CD ; Extended_Pictographic# 5.2 [10] (⛄..⛍) snowman without snow..DISABLED CAR
26CE ; Extended_Pictographic# 6.0 [1] (⛎) Ophiuchus
-26CF..26E1 ; Extended_Pictographic# 5.2 [19] (⛏️..⛡️) pick..RESTRICTED LEFT ENTRY-2
-26E2 ; Extended_Pictographic# 6.0 [1] (⛢️) ASTRONOMICAL SYMBOL FOR URANUS
-26E3 ; Extended_Pictographic# 5.2 [1] (⛣️) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
-26E4..26E7 ; Extended_Pictographic# 6.0 [4] (⛤️..⛧️) PENTAGRAM..INVERTED PENTAGRAM
-26E8..26FF ; Extended_Pictographic# 5.2 [24] (⛨️..⛿️) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
-2700 ; Extended_Pictographic# 7.0 [1] (✀️) BLACK SAFETY SCISSORS
-2701..2704 ; Extended_Pictographic# 1.1 [4] (✁️..✄️) UPPER BLADE SCISSORS..WHITE SCISSORS
-2705 ; Extended_Pictographic# 6.0 [1] (✅) white heavy check mark
+26CF..26E1 ; Extended_Pictographic# 5.2 [19] (⛏️..⛡) pick..RESTRICTED LEFT ENTRY-2
+26E2 ; Extended_Pictographic# 6.0 [1] (⛢) ASTRONOMICAL SYMBOL FOR URANUS
+26E3 ; Extended_Pictographic# 5.2 [1] (⛣) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
+26E4..26E7 ; Extended_Pictographic# 6.0 [4] (⛤..⛧) PENTAGRAM..INVERTED PENTAGRAM
+26E8..26FF ; Extended_Pictographic# 5.2 [24] (⛨..⛿) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
+2700 ; Extended_Pictographic# 7.0 [1] (✀) BLACK SAFETY SCISSORS
+2701..2704 ; Extended_Pictographic# 1.1 [4] (✁..✄) UPPER BLADE SCISSORS..WHITE SCISSORS
+2705 ; Extended_Pictographic# 6.0 [1] (✅) check mark button
2708..2709 ; Extended_Pictographic# 1.1 [2] (✈️..✉️) airplane..envelope
270A..270B ; Extended_Pictographic# 6.0 [2] (✊..✋) raised fist..raised hand
270C..2712 ; Extended_Pictographic# 1.1 [7] (✌️..✒️) victory hand..black nib
-2714 ; Extended_Pictographic# 1.1 [1] (✔️) heavy check mark
-2716 ; Extended_Pictographic# 1.1 [1] (✖️) heavy multiplication x
+2714 ; Extended_Pictographic# 1.1 [1] (✔️) check mark
+2716 ; Extended_Pictographic# 1.1 [1] (✖️) multiplication sign
271D ; Extended_Pictographic# 1.1 [1] (✝️) latin cross
2721 ; Extended_Pictographic# 1.1 [1] (✡️) star of David
2728 ; Extended_Pictographic# 6.0 [1] (✨) sparkles
@@ -537,8 +571,8 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
274E ; Extended_Pictographic# 6.0 [1] (❎) cross mark button
2753..2755 ; Extended_Pictographic# 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Extended_Pictographic# 5.2 [1] (❗) exclamation mark
-2763..2767 ; Extended_Pictographic# 1.1 [5] (❣️..❧️) heavy heart exclamation..ROTATED FLORAL HEART BULLET
-2795..2797 ; Extended_Pictographic# 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2763..2767 ; Extended_Pictographic# 1.1 [5] (❣️..❧) heart exclamation..ROTATED FLORAL HEART BULLET
+2795..2797 ; Extended_Pictographic# 6.0 [3] (➕..➗) plus sign..division sign
27A1 ; Extended_Pictographic# 1.1 [1] (➡️) right arrow
27B0 ; Extended_Pictographic# 6.0 [1] (➰) curly loop
27BF ; Extended_Pictographic# 6.0 [1] (➿) double curly loop
@@ -546,45 +580,46 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
2B05..2B07 ; Extended_Pictographic# 4.0 [3] (⬅️..⬇️) left arrow..down arrow
2B1B..2B1C ; Extended_Pictographic# 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Extended_Pictographic# 5.1 [1] (⭐) star
-2B55 ; Extended_Pictographic# 5.2 [1] (⭕) heavy large circle
+2B55 ; Extended_Pictographic# 5.2 [1] (⭕) hollow red circle
3030 ; Extended_Pictographic# 1.1 [1] (〰️) wavy dash
303D ; Extended_Pictographic# 3.2 [1] (〽️) part alternation mark
3297 ; Extended_Pictographic# 1.1 [1] (㊗️) Japanese “congratulations” button
3299 ; Extended_Pictographic# 1.1 [1] (㊙️) Japanese “secret” button
-1F000..1F02B ; Extended_Pictographic# 5.1 [44] (🀀️..🀫️) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
-1F02C..1F02F ; Extended_Pictographic# NA [4] (🀬️..🀯️) <reserved-1F02C>..<reserved-1F02F>
-1F030..1F093 ; Extended_Pictographic# 5.1[100] (🀰️..🂓️) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
-1F094..1F09F ; Extended_Pictographic# NA [12] (🂔️..🂟️) <reserved-1F094>..<reserved-1F09F>
-1F0A0..1F0AE ; Extended_Pictographic# 6.0 [15] (🂠️..🂮️) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
-1F0AF..1F0B0 ; Extended_Pictographic# NA [2] (🂯️..🂰️) <reserved-1F0AF>..<reserved-1F0B0>
-1F0B1..1F0BE ; Extended_Pictographic# 6.0 [14] (🂱️..🂾️) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
-1F0BF ; Extended_Pictographic# 7.0 [1] (🂿️) PLAYING CARD RED JOKER
-1F0C0 ; Extended_Pictographic# NA [1] (🃀️) <reserved-1F0C0>
-1F0C1..1F0CF ; Extended_Pictographic# 6.0 [15] (🃁️..🃏) PLAYING CARD ACE OF DIAMONDS..joker
-1F0D0 ; Extended_Pictographic# NA [1] (🃐️) <reserved-1F0D0>
-1F0D1..1F0DF ; Extended_Pictographic# 6.0 [15] (🃑️..🃟️) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
-1F0E0..1F0F5 ; Extended_Pictographic# 7.0 [22] (🃠️..🃵️) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
-1F0F6..1F0FF ; Extended_Pictographic# NA [10] (🃶️..🃿️) <reserved-1F0F6>..<reserved-1F0FF>
-1F10D..1F10F ; Extended_Pictographic# NA [3] (🄍️..🄏️) <reserved-1F10D>..<reserved-1F10F>
-1F12F ; Extended_Pictographic# 11.0 [1] (🄯️) COPYLEFT SYMBOL
-1F16C..1F16F ; Extended_Pictographic# NA [4] (🅬️..🅯️) <reserved-1F16C>..<reserved-1F16F>
+1F000..1F02B ; Extended_Pictographic# 5.1 [44] (🀀..🀫) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
+1F02C..1F02F ; Extended_Pictographic# NA [4] (🀬..🀯) <reserved-1F02C>..<reserved-1F02F>
+1F030..1F093 ; Extended_Pictographic# 5.1[100] (🀰..🂓) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+1F094..1F09F ; Extended_Pictographic# NA [12] (🂔..🂟) <reserved-1F094>..<reserved-1F09F>
+1F0A0..1F0AE ; Extended_Pictographic# 6.0 [15] (🂠..🂮) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
+1F0AF..1F0B0 ; Extended_Pictographic# NA [2] (🂯..🂰) <reserved-1F0AF>..<reserved-1F0B0>
+1F0B1..1F0BE ; Extended_Pictographic# 6.0 [14] (🂱..🂾) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
+1F0BF ; Extended_Pictographic# 7.0 [1] (🂿) PLAYING CARD RED JOKER
+1F0C0 ; Extended_Pictographic# NA [1] (🃀) <reserved-1F0C0>
+1F0C1..1F0CF ; Extended_Pictographic# 6.0 [15] (🃁..🃏) PLAYING CARD ACE OF DIAMONDS..joker
+1F0D0 ; Extended_Pictographic# NA [1] (🃐) <reserved-1F0D0>
+1F0D1..1F0DF ; Extended_Pictographic# 6.0 [15] (🃑..🃟) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
+1F0E0..1F0F5 ; Extended_Pictographic# 7.0 [22] (🃠..🃵) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
+1F0F6..1F0FF ; Extended_Pictographic# NA [10] (🃶..🃿) <reserved-1F0F6>..<reserved-1F0FF>
+1F10D..1F10F ; Extended_Pictographic# NA [3] (🄍..🄏) <reserved-1F10D>..<reserved-1F10F>
+1F12F ; Extended_Pictographic# 11.0 [1] (🄯) COPYLEFT SYMBOL
+1F16C ; Extended_Pictographic# 12.0 [1] (🅬) RAISED MR SIGN
+1F16D..1F16F ; Extended_Pictographic# NA [3] (🅭..🅯) <reserved-1F16D>..<reserved-1F16F>
1F170..1F171 ; Extended_Pictographic# 6.0 [2] (🅰️..🅱️) A button (blood type)..B button (blood type)
1F17E ; Extended_Pictographic# 6.0 [1] (🅾️) O button (blood type)
1F17F ; Extended_Pictographic# 5.2 [1] (🅿️) P button
1F18E ; Extended_Pictographic# 6.0 [1] (🆎) AB button (blood type)
1F191..1F19A ; Extended_Pictographic# 6.0 [10] (🆑..🆚) CL button..VS button
-1F1AD..1F1E5 ; Extended_Pictographic# NA [57] (🆭️..🇥️) <reserved-1F1AD>..<reserved-1F1E5>
+1F1AD..1F1E5 ; Extended_Pictographic# NA [57] (🆭..🇥) <reserved-1F1AD>..<reserved-1F1E5>
1F201..1F202 ; Extended_Pictographic# 6.0 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button
-1F203..1F20F ; Extended_Pictographic# NA [13] (🈃️..🈏️) <reserved-1F203>..<reserved-1F20F>
+1F203..1F20F ; Extended_Pictographic# NA [13] (🈃..🈏) <reserved-1F203>..<reserved-1F20F>
1F21A ; Extended_Pictographic# 5.2 [1] (🈚) Japanese “free of charge” button
1F22F ; Extended_Pictographic# 5.2 [1] (🈯) Japanese “reserved” button
1F232..1F23A ; Extended_Pictographic# 6.0 [9] (🈲..🈺) Japanese “prohibited” button..Japanese “open for business” button
-1F23C..1F23F ; Extended_Pictographic# NA [4] (🈼️..🈿️) <reserved-1F23C>..<reserved-1F23F>
-1F249..1F24F ; Extended_Pictographic# NA [7] (🉉️..🉏️) <reserved-1F249>..<reserved-1F24F>
+1F23C..1F23F ; Extended_Pictographic# NA [4] (🈼..🈿) <reserved-1F23C>..<reserved-1F23F>
+1F249..1F24F ; Extended_Pictographic# NA [7] (🉉..🉏) <reserved-1F249>..<reserved-1F24F>
1F250..1F251 ; Extended_Pictographic# 6.0 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button
-1F252..1F25F ; Extended_Pictographic# NA [14] (🉒️..🉟️) <reserved-1F252>..<reserved-1F25F>
-1F260..1F265 ; Extended_Pictographic# 10.0 [6] (🉠️..🉥️) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
-1F266..1F2FF ; Extended_Pictographic# NA[154] (🉦️..🋿️) <reserved-1F266>..<reserved-1F2FF>
+1F252..1F25F ; Extended_Pictographic# NA [14] (🉒..🉟) <reserved-1F252>..<reserved-1F25F>
+1F260..1F265 ; Extended_Pictographic# 10.0 [6] (🉠..🉥) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
+1F266..1F2FF ; Extended_Pictographic# NA[154] (🉦..🋿) <reserved-1F266>..<reserved-1F2FF>
1F300..1F320 ; Extended_Pictographic# 6.0 [33] (🌀..🌠) cyclone..shooting star
1F321..1F32C ; Extended_Pictographic# 7.0 [12] (🌡️..🌬️) thermometer..wind face
1F32D..1F32F ; Extended_Pictographic# 8.0 [3] (🌭..🌯) hot dog..burrito
@@ -594,7 +629,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F37D ; Extended_Pictographic# 7.0 [1] (🍽️) fork and knife with plate
1F37E..1F37F ; Extended_Pictographic# 8.0 [2] (🍾..🍿) bottle with popping cork..popcorn
1F380..1F393 ; Extended_Pictographic# 6.0 [20] (🎀..🎓) ribbon..graduation cap
-1F394..1F39F ; Extended_Pictographic# 7.0 [12] (🎔️..🎟️) HEART WITH TIP ON THE LEFT..admission tickets
+1F394..1F39F ; Extended_Pictographic# 7.0 [12] (🎔..🎟️) HEART WITH TIP ON THE LEFT..admission tickets
1F3A0..1F3C4 ; Extended_Pictographic# 6.0 [37] (🎠..🏄) carousel horse..person surfing
1F3C5 ; Extended_Pictographic# 7.0 [1] (🏅) sports medal
1F3C6..1F3CA ; Extended_Pictographic# 6.0 [5] (🏆..🏊) trophy..person swimming
@@ -602,7 +637,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F3CF..1F3D3 ; Extended_Pictographic# 8.0 [5] (🏏..🏓) cricket game..ping pong
1F3D4..1F3DF ; Extended_Pictographic# 7.0 [12] (🏔️..🏟️) snow-capped mountain..stadium
1F3E0..1F3F0 ; Extended_Pictographic# 6.0 [17] (🏠..🏰) house..castle
-1F3F1..1F3F7 ; Extended_Pictographic# 7.0 [7] (🏱️..🏷️) WHITE PENNANT..label
+1F3F1..1F3F7 ; Extended_Pictographic# 7.0 [7] (🏱..🏷️) WHITE PENNANT..label
1F3F8..1F3FA ; Extended_Pictographic# 8.0 [3] (🏸..🏺) badminton..amphora
1F400..1F43E ; Extended_Pictographic# 6.0 [63] (🐀..🐾) rat..paw prints
1F43F ; Extended_Pictographic# 7.0 [1] (🐿️) chipmunk
@@ -611,15 +646,15 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F442..1F4F7 ; Extended_Pictographic# 6.0[182] (👂..📷) ear..camera
1F4F8 ; Extended_Pictographic# 7.0 [1] (📸) camera with flash
1F4F9..1F4FC ; Extended_Pictographic# 6.0 [4] (📹..📼) video camera..videocassette
-1F4FD..1F4FE ; Extended_Pictographic# 7.0 [2] (📽️..📾️) film projector..PORTABLE STEREO
+1F4FD..1F4FE ; Extended_Pictographic# 7.0 [2] (📽️..📾) film projector..PORTABLE STEREO
1F4FF ; Extended_Pictographic# 8.0 [1] (📿) prayer beads
1F500..1F53D ; Extended_Pictographic# 6.0 [62] (🔀..🔽) shuffle tracks button..downwards button
-1F546..1F54A ; Extended_Pictographic# 7.0 [5] (🕆️..🕊️) WHITE LATIN CROSS..dove
-1F54B..1F54F ; Extended_Pictographic# 8.0 [5] (🕋..🕏️) kaaba..BOWL OF HYGIEIA
+1F546..1F54A ; Extended_Pictographic# 7.0 [5] (🕆..🕊️) WHITE LATIN CROSS..dove
+1F54B..1F54F ; Extended_Pictographic# 8.0 [5] (🕋..🕏) kaaba..BOWL OF HYGIEIA
1F550..1F567 ; Extended_Pictographic# 6.0 [24] (🕐..🕧) one o’clock..twelve-thirty
-1F568..1F579 ; Extended_Pictographic# 7.0 [18] (🕨️..🕹️) RIGHT SPEAKER..joystick
+1F568..1F579 ; Extended_Pictographic# 7.0 [18] (🕨..🕹️) RIGHT SPEAKER..joystick
1F57A ; Extended_Pictographic# 9.0 [1] (🕺) man dancing
-1F57B..1F5A3 ; Extended_Pictographic# 7.0 [41] (🕻️..🖣️) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
+1F57B..1F5A3 ; Extended_Pictographic# 7.0 [41] (🕻..🖣) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
1F5A4 ; Extended_Pictographic# 9.0 [1] (🖤) black heart
1F5A5..1F5FA ; Extended_Pictographic# 7.0 [86] (🖥️..🗺️) desktop computer..world map
1F5FB..1F5FF ; Extended_Pictographic# 6.0 [5] (🗻..🗿) mount fuji..moai
@@ -644,32 +679,37 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F62E..1F62F ; Extended_Pictographic# 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Extended_Pictographic# 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Extended_Pictographic# 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Extended_Pictographic# 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Extended_Pictographic# 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Extended_Pictographic# 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Extended_Pictographic# 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Extended_Pictographic# 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
1F680..1F6C5 ; Extended_Pictographic# 6.0 [70] (🚀..🛅) rocket..left luggage
-1F6C6..1F6CF ; Extended_Pictographic# 7.0 [10] (🛆️..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed
+1F6C6..1F6CF ; Extended_Pictographic# 7.0 [10] (🛆..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed
1F6D0 ; Extended_Pictographic# 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Extended_Pictographic# 9.0 [2] (🛑..🛒) stop sign..shopping cart
-1F6D3..1F6D4 ; Extended_Pictographic# 10.0 [2] (🛓️..🛔️) STUPA..PAGODA
-1F6D5..1F6DF ; Extended_Pictographic# NA [11] (🛕️..🛟️) <reserved-1F6D5>..<reserved-1F6DF>
+1F6D3..1F6D4 ; Extended_Pictographic# 10.0 [2] (🛓..🛔) STUPA..PAGODA
+1F6D5 ; Extended_Pictographic# 12.0 [1] (🛕) hindu temple
+1F6D6..1F6DF ; Extended_Pictographic# NA [10] (🛖..🛟) <reserved-1F6D6>..<reserved-1F6DF>
1F6E0..1F6EC ; Extended_Pictographic# 7.0 [13] (🛠️..🛬) hammer and wrench..airplane arrival
-1F6ED..1F6EF ; Extended_Pictographic# NA [3] (🛭️..🛯️) <reserved-1F6ED>..<reserved-1F6EF>
+1F6ED..1F6EF ; Extended_Pictographic# NA [3] (🛭..🛯) <reserved-1F6ED>..<reserved-1F6EF>
1F6F0..1F6F3 ; Extended_Pictographic# 7.0 [4] (🛰️..🛳️) satellite..passenger ship
1F6F4..1F6F6 ; Extended_Pictographic# 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Extended_Pictographic# 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Extended_Pictographic# 11.0 [1] (🛹) skateboard
-1F6FA..1F6FF ; Extended_Pictographic# NA [6] (🛺️..🛿️) <reserved-1F6FA>..<reserved-1F6FF>
-1F774..1F77F ; Extended_Pictographic# NA [12] (🝴️..🝿️) <reserved-1F774>..<reserved-1F77F>
-1F7D5..1F7D8 ; Extended_Pictographic# 11.0 [4] (🟕️..🟘️) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
-1F7D9..1F7FF ; Extended_Pictographic# NA [39] (🟙️..🟿️) <reserved-1F7D9>..<reserved-1F7FF>
-1F80C..1F80F ; Extended_Pictographic# NA [4] (🠌️..🠏️) <reserved-1F80C>..<reserved-1F80F>
-1F848..1F84F ; Extended_Pictographic# NA [8] (🡈️..🡏️) <reserved-1F848>..<reserved-1F84F>
-1F85A..1F85F ; Extended_Pictographic# NA [6] (🡚️..🡟️) <reserved-1F85A>..<reserved-1F85F>
-1F888..1F88F ; Extended_Pictographic# NA [8] (🢈️..🢏️) <reserved-1F888>..<reserved-1F88F>
-1F8AE..1F8FF ; Extended_Pictographic# NA [82] (🢮️..🣿️) <reserved-1F8AE>..<reserved-1F8FF>
-1F90C..1F90F ; Extended_Pictographic# NA [4] (🤌️..🤏️) <reserved-1F90C>..<reserved-1F90F>
+1F6FA ; Extended_Pictographic# 12.0 [1] (🛺) auto rickshaw
+1F6FB..1F6FF ; Extended_Pictographic# NA [5] (🛻..🛿) <reserved-1F6FB>..<reserved-1F6FF>
+1F774..1F77F ; Extended_Pictographic# NA [12] (🝴..🝿) <reserved-1F774>..<reserved-1F77F>
+1F7D5..1F7D8 ; Extended_Pictographic# 11.0 [4] (🟕..🟘) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
+1F7D9..1F7DF ; Extended_Pictographic# NA [7] (🟙..🟟) <reserved-1F7D9>..<reserved-1F7DF>
+1F7E0..1F7EB ; Extended_Pictographic# 12.0 [12] (🟠..🟫) orange circle..brown square
+1F7EC..1F7FF ; Extended_Pictographic# NA [20] (🟬..🟿) <reserved-1F7EC>..<reserved-1F7FF>
+1F80C..1F80F ; Extended_Pictographic# NA [4] (🠌..🠏) <reserved-1F80C>..<reserved-1F80F>
+1F848..1F84F ; Extended_Pictographic# NA [8] (🡈..🡏) <reserved-1F848>..<reserved-1F84F>
+1F85A..1F85F ; Extended_Pictographic# NA [6] (🡚..🡟) <reserved-1F85A>..<reserved-1F85F>
+1F888..1F88F ; Extended_Pictographic# NA [8] (🢈..🢏) <reserved-1F888>..<reserved-1F88F>
+1F8AE..1F8FF ; Extended_Pictographic# NA [82] (🢮..🣿) <reserved-1F8AE>..<reserved-1F8FF>
+1F90C ; Extended_Pictographic# NA [1] (🤌) <reserved-1F90C>
+1F90D..1F90F ; Extended_Pictographic# 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Extended_Pictographic# 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Extended_Pictographic# 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Extended_Pictographic# 10.0 [1] (🤟) love-you gesture
@@ -679,35 +719,50 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F931..1F932 ; Extended_Pictographic# 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Extended_Pictographic# 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Extended_Pictographic# 9.0 [3] (🤼..🤾) people wrestling..person playing handball
-1F93F ; Extended_Pictographic# NA [1] (🤿️) <reserved-1F93F>
+1F93F ; Extended_Pictographic# 12.0 [1] (🤿) diving mask
1F940..1F945 ; Extended_Pictographic# 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Extended_Pictographic# 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Extended_Pictographic# 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Extended_Pictographic# 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Extended_Pictographic# 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Extended_Pictographic# 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Extended_Pictographic# 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
-1F971..1F972 ; Extended_Pictographic# NA [2] (🥱️..🥲️) <reserved-1F971>..<reserved-1F972>
+1F96C..1F970 ; Extended_Pictographic# 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Extended_Pictographic# 12.0 [1] (🥱) yawning face
+1F972 ; Extended_Pictographic# NA [1] (🥲) <reserved-1F972>
1F973..1F976 ; Extended_Pictographic# 11.0 [4] (🥳..🥶) partying face..cold face
-1F977..1F979 ; Extended_Pictographic# NA [3] (🥷️..🥹️) <reserved-1F977>..<reserved-1F979>
+1F977..1F979 ; Extended_Pictographic# NA [3] (🥷..🥹) <reserved-1F977>..<reserved-1F979>
1F97A ; Extended_Pictographic# 11.0 [1] (🥺) pleading face
-1F97B ; Extended_Pictographic# NA [1] (🥻️) <reserved-1F97B>
-1F97C..1F97F ; Extended_Pictographic# 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Extended_Pictographic# 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Extended_Pictographic# 12.0 [1] (🥻) sari
+1F97C..1F97F ; Extended_Pictographic# 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Extended_Pictographic# 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Extended_Pictographic# 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Extended_Pictographic# 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Extended_Pictographic# 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9A3..1F9AF ; Extended_Pictographic# NA [13] (🦣️..🦯️) <reserved-1F9A3>..<reserved-1F9AF>
-1F9B0..1F9B9 ; Extended_Pictographic# 11.0 [10] (🦰..🦹) red-haired..supervillain
-1F9BA..1F9BF ; Extended_Pictographic# NA [6] (🦺️..🦿️) <reserved-1F9BA>..<reserved-1F9BF>
+1F9A3..1F9A4 ; Extended_Pictographic# NA [2] (🦣..🦤) <reserved-1F9A3>..<reserved-1F9A4>
+1F9A5..1F9AA ; Extended_Pictographic# 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AB..1F9AD ; Extended_Pictographic# NA [3] (🦫..🦭) <reserved-1F9AB>..<reserved-1F9AD>
+1F9AE..1F9AF ; Extended_Pictographic# 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Extended_Pictographic# 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Extended_Pictographic# 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Extended_Pictographic# 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Extended_Pictographic# 11.0 [2] (🧁..🧂) cupcake..salt
-1F9C3..1F9CF ; Extended_Pictographic# NA [13] (🧃️..🧏️) <reserved-1F9C3>..<reserved-1F9CF>
+1F9C3..1F9CA ; Extended_Pictographic# 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CB..1F9CC ; Extended_Pictographic# NA [2] (🧋..🧌) <reserved-1F9CB>..<reserved-1F9CC>
+1F9CD..1F9CF ; Extended_Pictographic# 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Extended_Pictographic# 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Extended_Pictographic# 11.0 [25] (🧧..🧿) red envelope..nazar amulet
-1FA00..1FA5F ; Extended_Pictographic# NA [96] (🨀️..🩟️) <reserved-1FA00>..<reserved-1FA5F>
-1FA60..1FA6D ; Extended_Pictographic# 11.0 [14] (🩠️..🩭️) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
-1FA6E..1FFFD ; Extended_Pictographic# NA[1424] (🩮️..🿽️) <reserved-1FA6E>..<reserved-1FFFD>
+1FA00..1FA53 ; Extended_Pictographic# 12.0 [84] (🨀..🩓) NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
+1FA54..1FA5F ; Extended_Pictographic# NA [12] (🩔..🩟) <reserved-1FA54>..<reserved-1FA5F>
+1FA60..1FA6D ; Extended_Pictographic# 11.0 [14] (🩠..🩭) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
+1FA6E..1FA6F ; Extended_Pictographic# NA [2] (🩮..🩯) <reserved-1FA6E>..<reserved-1FA6F>
+1FA70..1FA73 ; Extended_Pictographic# 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA74..1FA77 ; Extended_Pictographic# NA [4] (🩴..🩷) <reserved-1FA74>..<reserved-1FA77>
+1FA78..1FA7A ; Extended_Pictographic# 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA7B..1FA7F ; Extended_Pictographic# NA [5] (🩻..🩿) <reserved-1FA7B>..<reserved-1FA7F>
+1FA80..1FA82 ; Extended_Pictographic# 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA83..1FA8F ; Extended_Pictographic# NA [13] (🪃..🪏) <reserved-1FA83>..<reserved-1FA8F>
+1FA90..1FA95 ; Extended_Pictographic# 12.0 [6] (🪐..🪕) ringed planet..banjo
+1FA96..1FFFD ; Extended_Pictographic# NA[1384] (🪖..🿽) <reserved-1FA96>..<reserved-1FFFD>
# Total elements: 3793
diff --git a/lib/stdlib/uc_spec/gen_unicode_mod.escript b/lib/stdlib/uc_spec/gen_unicode_mod.escript
index de67b18afc..d820b9ed8e 100644
--- a/lib/stdlib/uc_spec/gen_unicode_mod.escript
+++ b/lib/stdlib/uc_spec/gen_unicode_mod.escript
@@ -191,7 +191,7 @@ gen_static(Fd) ->
" {U,L} -> #{upper=>U,lower=>L,title=>U,fold=>L};\n"
" {U,L,T,F} -> #{upper=>U,lower=>L,title=>T,fold=>F}\n"
" end.\n\n"),
- io:put_chars(Fd, "spec_version() -> {11,0}.\n\n\n"),
+ io:put_chars(Fd, "spec_version() -> {12,1}.\n\n\n"),
io:put_chars(Fd, "class(Codepoint) -> {CCC,_,_} = unicode_table(Codepoint),\n CCC.\n\n"),
io:put_chars(Fd, "-spec uppercase(unicode:chardata()) -> "
"maybe_improper_list(gc(),unicode:chardata()).\n"),
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 02eee400bf..e234b0cd58 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.12.1
+STDLIB_VSN = 3.14.1