diff options
53 files changed, 1604 insertions, 827 deletions
diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 0f09ab3d4d..1d1f2b44bc 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -111,6 +111,8 @@ jobs: tar -xzf ./otp_src.tar.gz cd otp export ERL_TOP=`pwd` + export MAKEFLAGS=-j4 + export ERLC_USE_SERVER=true eval `./otp_build env_win32 x64` ./otp_build configure if cat erts/CONF_INFO || cat lib/*/CONF_INFO || cat lib/*/SKIP || cat lib/SKIP-APPLICATIONS; then exit 1; fi @@ -232,8 +234,10 @@ jobs: TAG=${GITHUB_REF#refs/tags/} IS_RELEASE=`$(echo $TAG | grep -E '^OTP-[0-9]+\.[0-9]+$' > /dev/null) \ && echo "true" || echo "false"` + VSN=${TAG#OTP-} echo "::set-output name=tag::${TAG}" echo "::set-output name=release::${IS_RELEASE}" + echo "::set-output name=vsn::${VSN}" - uses: actions/checkout@v2 diff --git a/Makefile.in b/Makefile.in index ab2fc77b06..bf867f66ce 100644 --- a/Makefile.in +++ b/Makefile.in @@ -451,7 +451,7 @@ endif $(DOCGEN)/priv/bin/validate_links.escript $(ERL_TOP) make/$(TARGET)/mod2app.xml \ lib/*/doc/xml/*.xml erts/doc/xml/*.xml system/doc/xml/*/*.xml -mod2app: $(ERL_TOP)/make/$(TARGET)/mod2app.xml +mod2app: doc_bootstrap_build doc_bootstrap_copy $(ERL_TOP)/make/$(TARGET)/mod2app.xml $(ERL_TOP)/make/$(TARGET)/mod2app.xml: erts/doc/src/Makefile lib/*/doc/src/Makefile PATH=$(BOOT_PREFIX)"$${PATH}" escript $(BOOTSTRAP_ROOT)/bootstrap/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml @@ -55,7 +55,7 @@ skip_applications= while test $# != 0; do case $1 in -srcdir=* | --srcdir=*) - user_srcdir=`expr -- "$1" : '[^=]*=\(.*\)'` + user_srcdir=`echo "$1" | sed 's/^[^=]*=//'` if test "$ERL_TOP" != ""; then echo "WARNING: Overriding ERL_TOP with $user_srcdir" 1>&2 echo "" 1>&2 @@ -95,7 +95,7 @@ while test $# != 0; do echo "" 1>&2 ;; -cache-file=* | --cache-file=* ) - static_cache=`expr -- "$1" : '[^=]*=\(.*\)'` + static_cache=`echo "$1" | sed 's/^[^=]*=//'` if test "$static_cache" != "/dev/null"; then echo "WARNING: Only using config cache file '$static_cache' as static cache" 1>&2 echo "" 1>&2 @@ -140,8 +140,8 @@ while test $# != 0; do pie_ldflags="-no-pie" ;; CFLAGS=* | LDFLAGS=*) - flgs_var=`expr -- "$1" : '\([^=]*\)=.*'` - flgs_val=`expr -- "$1" : '[^=]*=\(.*\)'` + flgs_var=`echo "$1" | sed 's/=.*$//'` + flgs_val=`echo "$1" | sed 's/^[^=]*=//'` eval $flgs_var=\$flgs_val ;; --help=r* | -help=r*) @@ -151,7 +151,7 @@ while test $# != 0; do *) case $1 in --without-*) - skip_app=`expr -- "$1" : '--without-\(.*\)'` + skip_app=`echo "$1" | sed 's/^--without-//'` if [ "$skip_app" = "stdlib" ] || [ "$skip_app" = "kernel" ] || [ "$skip_app" = "sasl" ] || diff --git a/configure.src b/configure.src index 03bc4b3947..f0afd5c6ee 100644 --- a/configure.src +++ b/configure.src @@ -55,7 +55,7 @@ skip_applications= while test $# != 0; do case $1 in -srcdir=* | --srcdir=*) - user_srcdir=`expr -- "$1" : '[^=]*=\(.*\)'` + user_srcdir=`echo "$1" | sed 's/^[^=]*=//'` if test "$ERL_TOP" != ""; then echo "WARNING: Overriding ERL_TOP with $user_srcdir" 1>&2 echo "" 1>&2 @@ -95,7 +95,7 @@ while test $# != 0; do echo "" 1>&2 ;; -cache-file=* | --cache-file=* ) - static_cache=`expr -- "$1" : '[^=]*=\(.*\)'` + static_cache=`echo "$1" | sed 's/^[^=]*=//'` if test "$static_cache" != "/dev/null"; then echo "WARNING: Only using config cache file '$static_cache' as static cache" 1>&2 echo "" 1>&2 @@ -140,8 +140,8 @@ while test $# != 0; do pie_ldflags="-no-pie" ;; CFLAGS=* | LDFLAGS=*) - flgs_var=`expr -- "$1" : '\([^=]*\)=.*'` - flgs_val=`expr -- "$1" : '[^=]*=\(.*\)'` + flgs_var=`echo "$1" | sed 's/=.*$//'` + flgs_val=`echo "$1" | sed 's/^[^=]*=//'` eval $flgs_var=\$flgs_val ;; --help=r* | -help=r*) @@ -151,7 +151,7 @@ while test $# != 0; do *) case $1 in --without-*) - skip_app=`expr -- "$1" : '--without-\(.*\)'` + skip_app=`echo "$1" | sed 's/^--without-//'` if [ "$skip_app" = "stdlib" ] || [ "$skip_app" = "kernel" ] || [ "$skip_app" = "sasl" ] || diff --git a/erts/emulator/beam/beam_common.c b/erts/emulator/beam/beam_common.c index 5eeb9a9807..1a1326450f 100644 --- a/erts/emulator/beam/beam_common.c +++ b/erts/emulator/beam/beam_common.c @@ -1300,11 +1300,7 @@ apply_setup_error_handler(Process* p, Eterm module, Eterm function, Uint arity, * properly adjusted). */ - if (HeapWordsLeft(p) < sz) { - erts_garbage_collect(p, sz, reg, arity); - } - hp = HEAP_TOP(p); - HEAP_TOP(p) += sz; + hp = HAlloc(p, sz); for (i = arity-1; i >= 0; i--) { args = CONS(hp, reg[i], args); hp += 2; diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index e638e499c9..be4ee9772d 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -3285,11 +3285,10 @@ recv_marker_insert(Process *c_p, ErtsRecvMarker *markp, int setting) if (!setting && *c_p->sig_qs.save == (ErtsMessage *) &markp->sig) { /* - * This should most likely never happen (which is why the assert - * is here), but if it does, leave the message queue in a valid - * state... + * This can happen when a recv marker recently entered the message + * queue via erts_proc_sig_handle_incoming() through the midddle + * signal queue... */ - ASSERT(0); markp->pass++; c_p->sig_qs.save = c_p->sig_qs.last; } diff --git a/erts/emulator/beam/jit/x86/instr_call.cpp b/erts/emulator/beam/jit/x86/instr_call.cpp index b79ef5567c..e0ee3543bd 100644 --- a/erts/emulator/beam/jit/x86/instr_call.cpp +++ b/erts/emulator/beam/jit/x86/instr_call.cpp @@ -136,7 +136,7 @@ x86::Mem BeamModuleAssembler::emit_variable_apply(bool includeI) { align_erlang_cp(); a.bind(entry); - emit_enter_runtime<Update::eStack | Update::eHeap>(); + emit_enter_runtime<Update::eReductions | Update::eStack | Update::eHeap>(); a.mov(ARG1, c_p); load_x_reg_array(ARG2); @@ -151,7 +151,7 @@ x86::Mem BeamModuleAssembler::emit_variable_apply(bool includeI) { runtime_call<4>(apply); - emit_leave_runtime<Update::eStack | Update::eHeap>(); + emit_leave_runtime<Update::eReductions | Update::eStack | Update::eHeap>(); a.test(RET, RET); a.short_().jne(dispatch); @@ -185,7 +185,7 @@ x86::Mem BeamModuleAssembler::emit_fixed_apply(const ArgVal &Arity, mov_arg(ARG3, Arity); - emit_enter_runtime<Update::eStack | Update::eHeap>(); + emit_enter_runtime<Update::eReductions | Update::eStack | Update::eHeap>(); a.mov(ARG1, c_p); load_x_reg_array(ARG2); @@ -200,7 +200,7 @@ x86::Mem BeamModuleAssembler::emit_fixed_apply(const ArgVal &Arity, runtime_call<5>(fixed_apply); - emit_leave_runtime<Update::eStack | Update::eHeap>(); + emit_leave_runtime<Update::eReductions | Update::eStack | Update::eHeap>(); a.test(RET, RET); a.short_().jne(dispatch); diff --git a/erts/emulator/valgrind/suppress.standard b/erts/emulator/valgrind/suppress.standard index 85548e0869..51cb44e5ef 100644 --- a/erts/emulator/valgrind/suppress.standard +++ b/erts/emulator/valgrind/suppress.standard @@ -293,6 +293,38 @@ fun:process_main fun:sched_thread_func } { +Loading problem again. Now after PR +Memcheck:Leak +fun:malloc +... +fun:valid_curve +fun:init_curves +fun:get_curve_cnt +fun:init_curve_types +fun:init_algorithms_types +fun:initialize +fun:load +fun:erts_load_nif +fun:process_main +} +{ +Compiler before PR +Memcheck:Leak +fun:malloc +fun:erts_sys_alloc +fun:erts_alloc_fnf +fun:erl_drv_mutex_create +fun:enif_mutex_create +fun:init_algorithms_types +fun:initialize +fun:load +fun:erts_load_nif +fun:process_main +fun:sched_thread_func +fun:thr_wrapper +fun:start_thread +} +{ Crypto internal.. loading pecularities revisited Memcheck:Leak fun:malloc diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index 0b8657ced3..731e550364 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -955,9 +955,9 @@ Dir = string() Suites = atom() | [atom()] | all Suite = atom() - Groups = GroupPath | [GroupPath] | GroupSpec | [GroupSpec] | all - GroupPath = [GroupName] - GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec} + Groups = GroupPath | GroupSpec | [GroupSpec] | all + GroupPath = [[GroupSpec]] + GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,[GroupSpec]} GroupName = atom() GroupNames = GroupName | [GroupName] Cases = atom() | [atom()] | all @@ -1608,5 +1608,3 @@ div.error pre { color:white }</pre> </section> </chapter> - - diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl index b296939bd6..273a1acb12 100644 --- a/lib/common_test/src/ct_groups.erl +++ b/lib/common_test/src/ct_groups.erl @@ -550,11 +550,11 @@ search_and_override([Conf = {conf,Props,Init,Tests,End}], ORSpec, Mod) -> Suite = ?val(suite, Props), case lists:keysearch(Name, 1, ORSpec) of {value,{Name,default}} -> - [Conf]; + [{conf, Props, Init, search_and_override(Tests, ORSpec, Mod),End}]; {value,{Name,ORProps}} -> - [{conf,InsProps(Name,Suite,ORProps),Init,Tests,End}]; + [{conf,InsProps(Name,Suite,ORProps),Init, search_and_override(Tests, ORSpec, Mod),End}]; {value,{Name,default,[]}} -> - [Conf]; + [{conf, Props, Init, search_and_override(Tests, ORSpec, Mod),End}]; {value,{Name,default,SubORSpec}} -> override_props([Conf], SubORSpec, Name,Mod); {value,{Name,ORProps,SubORSpec}} -> @@ -562,7 +562,8 @@ search_and_override([Conf = {conf,Props,Init,Tests,End}], ORSpec, Mod) -> Init,Tests,End}], SubORSpec, Name,Mod); _ -> [{conf,Props,Init,search_and_override(Tests,ORSpec,Mod),End}] - end. + end; +search_and_override(Tests, _, _) -> Tests. %% Modify the Tests element according to the override specification override_props([{conf,Props,Init,Tests,End} | Confs], SubORSpec, Name,Mod) -> diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index a69267f5ea..27d0418029 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -2050,8 +2050,23 @@ final_tests1([{TestDir,Suite,GrsOrCs}|Tests], Final, Skip, Bad) when [ct_groups:make_conf(TestDir, Suite, GroupName, Props, TCs)]; ({GroupOrGroups,TCs}) -> - [ct_groups:make_conf(TestDir, Suite, - GroupOrGroups, [], TCs)]; + case GroupOrGroups of + [GroupList] when is_list(GroupList) -> + {GrpNames, Props} = lists:foldl( + fun({GrpName,_} = GrSpec, {GrpNames, Props}) -> + {lists:append(GrpNames, [GrpName]), [GrSpec | Props]}; + ({GrpName,_,_} = GrSpec, {GrpNames, Props}) -> + {lists:append(GrpNames, [GrpName]), [GrSpec | Props]}; + (GrpName, {GrpNames, Props}) -> + {lists:append(GrpNames, [GrpName]), Props} + end, + {[], []}, GroupList), + [ct_groups:make_conf(TestDir, Suite, + [GrpNames], [{override, Props}], TCs)]; + _ -> + [ct_groups:make_conf(TestDir, Suite, + GroupOrGroups, [], TCs)] + end; (TC) -> [TC] end, GrsOrCs), diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl index 2d2c42999f..41027dff36 100644 --- a/lib/common_test/test/ct_testspec_1_SUITE.erl +++ b/lib/common_test/test/ct_testspec_1_SUITE.erl @@ -63,6 +63,9 @@ all() -> [all_suites, skip_all_suites, suite, skip_suite, all_testcases, skip_all_testcases, testcase, skip_testcase, all_groups, skip_all_groups, group, + group_path, group_config, + group_spec, multi_group, multi_group_config, + groupspec_path, groupspec_path_2, skip_group, group_all_testcases, skip_group_all_testcases, group_testcase, skip_group_testcase, topgroup, subgroup, skip_subgroup, @@ -207,12 +210,53 @@ skip_all_groups(Config) when is_list(Config) -> group(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), - TestDir = filename:join(DataDir, "groups_1"), TestSpec = [{groups,TestDir,groups_11_SUITE,test_group_1a}], - setup_and_execute(group, TestSpec, Config). +group_path(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,[[test_group_2, test_group_3]]}], + setup_and_execute(group_path, TestSpec, Config). + +group_config(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE, {test_group_1c, [parallel]}}], + setup_and_execute(group_config, TestSpec, Config). + +group_spec(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE, {test_group_2, [parallel], [{test_group_3, [sequence]}]}}], + setup_and_execute(group_spec, TestSpec, Config). + +multi_group(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE, [test_group_1a, test_group_3]}], + setup_and_execute(multi_group, TestSpec, Config). + +multi_group_config(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE, [{test_group_2, [parallel], [{test_group_3, [sequence]}]}, + {test_group_9, [sequence], [{test_group_8, [parallel]}]}]}], + setup_and_execute(multi_group_config, TestSpec, Config). + +groupspec_path(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,[[{test_group_2, []}, {test_group_3,[]}]]}], + setup_and_execute(groupspec_path, TestSpec, Config). + +groupspec_path_2(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir, "groups_1"), + TestSpec = [{groups,TestDir,groups_11_SUITE,[[{test_group_2, [parallel]}, {test_group_3,[sequence]}]]}], + setup_and_execute(groupspec_path_2, TestSpec, Config). + skip_group(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), @@ -224,7 +268,6 @@ skip_group(Config) when is_list(Config) -> setup_and_execute(skip_group, TestSpec, Config). - %%%----------------------------------------------------------------- %%% @@ -753,7 +796,7 @@ test_events(all_groups) -> [ {?eh,start_logging,'_'}, {?eh,tc_start,{groups_11_SUITE,init_per_suite}}, - {?eh,test_stats,{12,0,{0,0}}}, + {?eh,test_stats,{16,0,{0,0}}}, {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}}, {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} ]; @@ -761,7 +804,7 @@ test_events(all_groups) -> test_events(skip_all_groups) -> [ {?eh,start_logging,'_'}, - {?eh,start_info,{1,1,12}}, + {?eh,start_info,{1,1,16}}, {?eh,tc_start,{groups_11_SUITE,init_per_suite}}, {?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1a},"SKIPPED!"}}, {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1a},"SKIPPED!"}}, @@ -775,26 +818,38 @@ test_events(skip_all_groups) -> {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1b},"SKIPPED!"}}, {?eh,test_stats,{0,0,{4,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1b},"SKIPPED!"}}, + {?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_1c},"SKIPPED!"}}, + {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1a,test_group_1c},"SKIPPED!"}}, + {?eh,test_stats,{0,0,{5,0}}}, + {?eh,tc_user_skip,{groups_11_SUITE,{testcase_1b,test_group_1c},"SKIPPED!"}}, + {?eh,test_stats,{0,0,{6,0}}}, + {?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_1c},"SKIPPED!"}}, {?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_2},"SKIPPED!"}}, {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2a,test_group_2},"SKIPPED!"}}, - {?eh,test_stats,{0,0,{5,0}}}, + {?eh,test_stats,{0,0,{7,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3a,test_group_3},"SKIPPED!"}}, - {?eh,test_stats,{0,0,{6,0}}}, + {?eh,test_stats,{0,0,{8,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{testcase_3b,test_group_3},"SKIPPED!"}}, - {?eh,test_stats,{0,0,{7,0}}}, + {?eh,test_stats,{0,0,{9,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{testcase_2b,test_group_2},"SKIPPED!"}}, - {?eh,test_stats,{0,0,{8,0}}}, + {?eh,test_stats,{0,0,{10,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_2},"SKIPPED!"}}, {?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_4},"SKIPPED!"}}, {?eh,tc_user_skip,{groups_11_SUITE,{testcase_5a,test_group_5},"SKIPPED!"}}, - {?eh,test_stats,{0,0,{9,0}}}, + {?eh,test_stats,{0,0,{11,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{testcase_7a,test_group_7},"SKIPPED!"}}, - {?eh,test_stats,{0,0,{10,0}}}, + {?eh,test_stats,{0,0,{12,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{testcase_7b,test_group_7},"SKIPPED!"}}, - {?eh,test_stats,{0,0,{11,0}}}, + {?eh,test_stats,{0,0,{13,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{testcase_5b,test_group_5},"SKIPPED!"}}, - {?eh,test_stats,{0,0,{12,0}}}, + {?eh,test_stats,{0,0,{14,0}}}, {?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_4},"SKIPPED!"}}, + {?eh,tc_user_skip,{groups_11_SUITE,{init_per_group,test_group_9},"SKIPPED!"}}, + {?eh,tc_user_skip,{groups_11_SUITE,{testcase_9,test_group_9},"SKIPPED!"}}, + {?eh,test_stats,{0,0,{15,0}}}, + {?eh,tc_user_skip,{groups_11_SUITE,{testcase_8,test_group_8},"SKIPPED!"}}, + {?eh,test_stats,{0,0,{16,0}}}, + {?eh,tc_user_skip,{groups_11_SUITE,{end_per_group,test_group_9},"SKIPPED!"}}, {?eh,tc_start,{groups_11_SUITE,end_per_suite}}, {?eh,tc_done,{groups_11_SUITE,end_per_suite,ok}}, {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} @@ -813,6 +868,65 @@ test_events(group) -> {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} ]; +test_events(group_path) -> + [ + {?eh,start_logging,'_'}, + {?eh,tc_start,{groups_11_SUITE,init_per_suite}}, + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_2,[sequence]}}}, + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_3,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_3,[parallel]},'_'}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_2,[sequence]},'_'}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}}, + {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} + ]; + +test_events(group_config) -> + [ + {?eh,start_logging,'_'}, + {?eh,tc_start,{groups_11_SUITE,init_per_suite}}, + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_1c,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1c,[parallel]},'_'}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}}, + {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} + ]; + + +test_events(groupspec_path) -> + [ + {?eh,start_logging,'_'}, + {?eh,tc_start,{groups_11_SUITE,init_per_suite}}, + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_2,[]}}}, + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_3,[]}}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3a}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3b}}, + {?eh,test_stats,{2,0,{0,0}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_3,[]},'_'}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_2,[]},'_'}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}}, + {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} + ]; + +test_events(groupspec_path_2) -> + [ + {?eh,start_logging,'_'}, + {?eh,tc_start,{groups_11_SUITE,init_per_suite}}, + {parallel, [ + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_2,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{init_per_group,test_group_2,[parallel]},ok}}, + [ + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_3,[sequence]}}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3a}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3b}}, + {?eh,test_stats,{2,0,{0,0}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_3,[sequence]},'_'}} + ], + {?eh,tc_start,{groups_11_SUITE,{end_per_group,test_group_2,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_2,[parallel]},ok}} + ]}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}}, + {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} + ]; + test_events(skip_group) -> [ {?eh,start_logging,'_'}, @@ -858,6 +972,104 @@ test_events(group_all_testcases) -> {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} ]; +test_events(group_spec) -> + [ + {?eh,start_logging,'_'}, + {?eh,tc_start,{groups_11_SUITE,init_per_suite}}, + {parallel, [ + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_2,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{init_per_group,test_group_2,[parallel]},ok}}, + {?eh,tc_start,{groups_11_SUITE,testcase_2a}}, + {?eh,tc_done,{groups_11_SUITE,testcase_2a,ok}}, + [ + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_3,[sequence]}}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3a}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3b}}, + {?eh,test_stats,{3,0,{0,0}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_3,[sequence]},'_'}} + ], + {?eh,tc_start,{groups_11_SUITE,testcase_2b}}, + {?eh,tc_done,{groups_11_SUITE,testcase_2b,ok}}, + {?eh,test_stats,{4,0,{0,0}}}, + {?eh,tc_start,{groups_11_SUITE,{end_per_group,test_group_2,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_2,[parallel]},ok}} + ]}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}}, + {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} + ]; + +test_events(multi_group) -> + [ + {?eh,start_logging,'_'}, + {?eh,tc_start,{groups_11_SUITE,init_per_suite}}, + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_1a,[]}}}, + {?eh,tc_start,{groups_11_SUITE,testcase_1a}}, + {?eh,tc_start,{groups_11_SUITE,testcase_1b}}, + {?eh,test_stats,{2,0,{0,0}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1a,[]},'_'}}, + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_2,[sequence]}}}, + {parallel, [ + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_3,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{init_per_group,test_group_3,[parallel]}, ok}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3a}}, + {?eh,tc_done,{groups_11_SUITE,testcase_3a,ok}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3b}}, + {?eh,tc_done,{groups_11_SUITE,testcase_3b,ok}}, + {?eh,test_stats,{4,0,{0,0}}}, + {?eh,tc_start,{groups_11_SUITE,{end_per_group,test_group_3,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_3,[parallel]}, ok}} + ]}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_2,[sequence]},'_'}}, + {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}}, + {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} + ]; + +test_events(multi_group_config) -> + [ + {?eh,start_logging,'_'}, + {?eh,tc_start,{groups_11_SUITE,init_per_suite}}, + {parallel, [ + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_2,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{init_per_group,test_group_2,[parallel]},ok}}, + {?eh,tc_start,{groups_11_SUITE,testcase_2a}}, + {?eh,tc_done,{groups_11_SUITE,testcase_2a,ok}}, + [ + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_3,[sequence]}}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3a}}, + {?eh,tc_start,{groups_11_SUITE,testcase_3b}}, + {?eh,test_stats,{3,0,{0,0}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_3,[sequence]},'_'}} + ], + {?eh,tc_start,{groups_11_SUITE,testcase_2b}}, + {?eh,tc_done,{groups_11_SUITE,testcase_2b,ok}}, + {?eh,test_stats,{4,0,{0,0}}}, + {?eh,tc_start,{groups_11_SUITE,{end_per_group,test_group_2,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_2,[parallel]},ok}} + ]}, + [ + {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_9,[sequence]}}}, + {?eh,tc_done,{groups_11_SUITE,{init_per_group,test_group_9,[sequence]},ok}}, + {?eh,tc_start,{groups_11_SUITE,testcase_9}}, + {?eh,tc_done,{groups_11_SUITE,testcase_9,ok}}, + {?eh,test_stats,{5,0,{0,0}}}, + {parallel,[ + {?eh,tc_start, + {groups_11_SUITE,{init_per_group,test_group_8,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{init_per_group,test_group_8,[parallel]},ok}}, + {?eh,tc_start,{groups_11_SUITE,testcase_8}}, + {?eh,tc_done,{groups_11_SUITE,testcase_8,ok}}, + {?eh,test_stats,{6,0,{0,0}}}, + {?eh,tc_start,{groups_11_SUITE,{end_per_group,test_group_8,[parallel]}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_8,[parallel]},ok}} + ]}, + {?eh,tc_start,{groups_11_SUITE,{end_per_group,test_group_9,[sequence]}}}, + {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_9,[sequence]},ok}} + ], + {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}}, + {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}} + ]; + + test_events(skip_group_all_testcases) -> [ {?eh,start_logging,'_'}, diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_11_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_11_SUITE.erl index 4d481fe3b8..56c97d9782 100644 --- a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_11_SUITE.erl +++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_11_SUITE.erl @@ -36,9 +36,11 @@ groups() -> {test_group_1b, [], [testcase_1a,testcase_1b]}, - {test_group_2, [], [testcase_2a, + {test_group_1c, [sequence], [testcase_1a,testcase_1b]}, - {test_group_3, [], [testcase_3a, + {test_group_2, [sequence], [testcase_2a, + + {test_group_3, [parallel], [testcase_3a, testcase_3b]}, testcase_2b]}, @@ -49,7 +51,12 @@ groups() -> testcase_5b]}]}, {test_group_6, [{group, test_group_7}]}, - {test_group_7, [testcase_7a,testcase_7b]} + {test_group_7, [testcase_7a,testcase_7b]}, + + {test_group_8, [sequence], [testcase_8]}, + + {test_group_9, [parallel], [testcase_9, {group, test_group_8}]} + ]. all() -> @@ -64,10 +71,11 @@ all() -> %% this func only for internal test purposes grs_and_tcs() -> {[ - test_group_1a, test_group_1b, + test_group_1a, test_group_1b, test_group_1c, test_group_2, test_group_3, test_group_4, test_group_5, - test_group_6, test_group_7 + test_group_6, test_group_7, + test_group_8, test_group_9 ], [ testcase_1, @@ -77,7 +85,9 @@ grs_and_tcs() -> testcase_3a, testcase_3b, testcase_3, testcase_5a, testcase_5b, - testcase_7a, testcase_7b + testcase_7a, testcase_7b, + testcase_8, + testcase_9 ]}. %%-------------------------------------------------------------------- @@ -95,7 +105,10 @@ end_per_suite(Config) -> %%-------------------------------------------------------------------- init_per_group(Group, Config) -> - [{name,Group}] = ?config(tc_group_properties,Config), + Group = case ?config(tc_group_properties,Config) of + [{name, Group0}] -> Group0; + [{name, Group0}, _Props] -> Group0 + end, {Grs,_} = grs_and_tcs(), case lists:member(Group, Grs) of true -> @@ -164,8 +177,11 @@ testcase_1a(Config) -> _ -> case ?config(test_group_1b,Config) of test_group_1b -> ok; + _ -> case ?config(test_group_1c,Config) of + test_group_1c -> ok; _ -> ct:fail(no_group_data) end + end end, testcase_1a = ?config(testcase_1a,Config), ok. @@ -178,8 +194,11 @@ testcase_1b(Config) -> _ -> case ?config(test_group_1b,Config) of test_group_1b -> ok; + _ -> case ?config(test_group_1c,Config) of + test_group_1c -> ok; _ -> ct:fail(no_group_data) end + end end, undefined = ?config(testcase_1a,Config), testcase_1b = ?config(testcase_1b,Config), @@ -191,6 +210,7 @@ testcase_2(Config) -> init = ?config(suite,Config), undefined = ?config(test_group_1a,Config), undefined = ?config(test_group_1b,Config), + undefined = ?config(test_group_1c, Config), testcase_2 = ?config(testcase_2,Config), ok. @@ -280,3 +300,19 @@ testcase_7b(Config) -> undefined = ?config(testcase_7a,Config), testcase_7b = ?config(testcase_7b,Config), ok. +testcase_8() -> + []. +testcase_8(Config) -> + init = ?config(suite,Config), + test_group_9 = ?config(test_group_9,Config), + test_group_8 = ?config(test_group_8,Config), + testcase_8 = ?config(testcase_8,Config), + ok. +testcase_9() -> + []. +testcase_9(Config) -> + init = ?config(suite,Config), + test_group_9 = ?config(test_group_9,Config), + undefined = ?config(test_group_8,Config), + testcase_9 = ?config(testcase_9,Config), + ok. diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index d360d9b6cb..92918b5d9f 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -93,6 +93,7 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/bn$(TYPEMARKER).o \ $(OBJDIR)/cipher$(TYPEMARKER).o \ $(OBJDIR)/cmac$(TYPEMARKER).o \ + $(OBJDIR)/common$(TYPEMARKER).o \ $(OBJDIR)/dh$(TYPEMARKER).o \ $(OBJDIR)/digest$(TYPEMARKER).o \ $(OBJDIR)/dss$(TYPEMARKER).o \ @@ -181,7 +182,7 @@ static_lib: $(NIF_ARCHIVE) $(OBJDIR)/otp_test_engine$(TYPEMARKER).o: otp_test_engine.c $(V_at)$(INSTALL_DIR) $(OBJDIR) - $(V_CC) -c -o $@ $(ALL_CFLAGS) $< + $(V_CC) -c -o $@ $(filter-out -Wmissing-prototypes,$(ALL_CFLAGS)) $< $(LIBDIR)/otp_test_engine$(TYPEMARKER).so: $(TEST_ENGINE_OBJS) $(V_at)$(INSTALL_DIR) $(LIBDIR) diff --git a/lib/crypto/c_src/aead.c b/lib/crypto/c_src/aead.c index 42c3d35928..8bbeb5bec4 100644 --- a/lib/crypto/c_src/aead.c +++ b/lib/crypto/c_src/aead.c @@ -51,30 +51,30 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] encflg = -1; else { - ret = EXCP_BADARG(env, "Bad enc flag"); + ret = EXCP_BADARG_N(env, 6, "Bad enc flag"); goto done; } type = argv[0]; if (!enif_is_atom(env, type)) - {ret = EXCP_BADARG(env, "non-atom cipher type"); goto done;} + {ret = EXCP_BADARG_N(env, 0, "non-atom cipher type"); goto done;} if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) - {ret = EXCP_BADARG(env, "non-binary key"); goto done;} + {ret = EXCP_BADARG_N(env, 1, "non-binary key"); goto done;} if (!enif_inspect_iolist_as_binary(env, argv[2], &iv)) - {ret = EXCP_BADARG(env, "non-binary iv"); goto done;} + {ret = EXCP_BADARG_N(env, 2, "non-binary iv"); goto done;} if (!enif_inspect_iolist_as_binary(env, argv[3], &in)) - {ret = EXCP_BADARG(env, "non-binary text"); goto done;} + {ret = EXCP_BADARG_N(env, 3, "non-binary text"); goto done;} if (!enif_inspect_iolist_as_binary(env, argv[4], &aad)) - {ret = EXCP_BADARG(env, "non-binary AAD"); goto done;} + {ret = EXCP_BADARG_N(env, 4, "non-binary AAD"); goto done;} if (encflg) { if (!enif_get_uint(env, argv[5], &tag_len)) - {ret = EXCP_BADARG(env, "Bad Tag length"); goto done;} + {ret = EXCP_BADARG_N(env, 5, "Bad Tag length"); goto done;} tag_data = NULL; } else { if (!enif_inspect_iolist_as_binary(env, argv[5], &tag)) - {ret = EXCP_BADARG(env, "non-binary Tag"); goto done;} + {ret = EXCP_BADARG_N(env, 5, "non-binary Tag"); goto done;} tag_len = tag.size; tag_data = tag.data; } @@ -84,16 +84,16 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] || iv.size > INT_MAX || in.size > INT_MAX || aad.size > INT_MAX) - {ret = EXCP_BADARG(env, "binary too long"); goto done;} + {ret = EXCP_BADARG_N(env, 5, "binary too long"); goto done;} if ((cipherp = get_cipher_type(type, key.size)) == NULL) - {ret = EXCP_BADARG(env, "Unknown cipher"); goto done;} + {ret = EXCP_BADARG_N(env, 0, "Unknown cipher or invalid key size"); goto done;} if (cipherp->flags & NON_EVP_CIPHER) - {ret = EXCP_BADARG(env, "Bad cipher"); goto done;} + {ret = EXCP_BADARG_N(env, 0, "Bad cipher"); goto done;} if (! (cipherp->flags & AEAD_CIPHER) ) - {ret = EXCP_BADARG(env, "Not aead cipher"); goto done;} + {ret = EXCP_BADARG_N(env, 0, "Not aead cipher"); goto done;} if ((cipher = cipherp->cipher.p) == NULL) - {ret = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version"); goto done;} + {ret = EXCP_NOTSUP_N(env, 0, "The cipher is not supported in this libcrypto version"); goto done;} #if defined(HAVE_GCM_EVP_DECRYPT_BUG) if ( !encflg && (cipherp->flags & GCM_MODE)) @@ -106,27 +106,27 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encflg) != 1) {ret = EXCP_ERROR(env, "CipherInit failed"); goto done;} if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1) - {ret = EXCP_BADARG(env, "Bad IV length"); goto done;} + {ret = EXCP_BADARG_N(env, 2, "Bad IV length"); goto done;} #if defined(HAVE_CCM) if (cipherp->flags & CCM_MODE) { if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_tag, (int)tag_len, tag_data) != 1) - {ret = EXCP_BADARG(env, "Can't set tag"); goto done;} + {ret = EXCP_BADARG_N(env, 5, "Can't set tag"); goto done;} if (EVP_CipherInit_ex(ctx, NULL, NULL, key.data, iv.data, -1) != 1) - {ret = EXCP_BADARG(env, "Can't set key or iv"); goto done;} + {ret = EXCP_ERROR(env, "Can't set key or iv"); goto done;} if (EVP_CipherUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1) - {ret = EXCP_BADARG(env, "Can't set text size"); goto done;} + {ret = EXCP_ERROR(env, "Can't set text size"); goto done;} } else #endif { /* GCM_MODE or CHACHA20_POLY1305 */ /* Set key and iv */ if (EVP_CipherInit_ex(ctx, NULL, NULL, key.data, iv.data, -1) != 1) - {ret = EXCP_BADARG(env, "Can't set key or iv"); goto done;} + {ret = EXCP_ERROR(env, "Can't set key and iv"); goto done;} } /* Set the AAD */ if (EVP_CipherUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1) - {ret = EXCP_BADARG(env, "Can't set AAD"); goto done;} + {ret = EXCP_BADARG_N(env, 4, "Can't set AAD"); goto done;} /* Set the plain text and get the crypto text (or vice versa :) ) */ if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL) @@ -134,7 +134,7 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] if (EVP_CipherUpdate(ctx, outp, &len, in.data, (int)in.size) != 1) { if (encflg) - ret = EXCP_BADARG(env, "Can't set in-text"); + ret = EXCP_BADARG_N(env, 3, "Can't set in-text"); else /* Decrypt error */ ret = atom_error; @@ -182,7 +182,7 @@ done: return ret; #else - return EXCP_NOTSUP(env, "Unsupported Cipher"); + return EXCP_NOTSUP_N(env, 0, "Unsupported Cipher"); #endif } diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c index c2ae10f2ac..35404ba1e0 100644 --- a/lib/crypto/c_src/api_ng.c +++ b/lib/crypto/c_src/api_ng.c @@ -85,13 +85,117 @@ int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in) /* Get the arguments for the initialization of the EVP_CIPHER_CTX. Check */ /* them and initialize that context. */ /*************************************************************************/ + +static ERL_NIF_TERM get_opts(ErlNifEnv* env, const ERL_NIF_TERM opts, int opts_arg_num, int *encflgp, ERL_NIF_TERM *padflgp) +{ /* boolean() | [{Tag,Val}] Tag = encrypt | padding */ + unsigned list_len; + ERL_NIF_TERM p, hd, tl; + + *padflgp = atom_false; /* Not valid as padding value */ + /* First check if the opts is an atom: */ + if (opts == atom_true) + { + *encflgp = 1; + *padflgp = atom_undefined; + return atom_ok; + } + + if (opts == atom_false) + { + *encflgp = 0; + *padflgp = atom_undefined; + return atom_ok; + } + + if (opts == atom_undefined) + /* For compat funcs in crypto.erl. TODO: check and remove */ + { + *encflgp = -1; + *padflgp = atom_undefined; + return atom_ok; + } + + if (!enif_is_list(env, opts) || !enif_get_list_length(env, opts, &list_len)) + /* Not a boolean() and not a list, definitly an error */ + return EXCP_BADARG_N(env, opts_arg_num, "Options are not a boolean or a proper list"); + + /* A list, might be a property list, as it should */ + *encflgp = -14; /* why not? */ + p = opts; + while (enif_get_list_cell(env, p, &hd, &tl)) + /* Loop through the list [{Tag,Value},...] + - check that + + the list is proper + + each list element is a 2-tuple + + the Tag is known + + the Value is of right type + - assign Values of known Tags to their respective flags + */ + { + int arity; + const ERL_NIF_TERM* elements; + + if (!(enif_get_tuple(env, hd, &arity, &elements) && (arity == 2)) ) + return EXCP_BADARG_N(env, opts_arg_num, "Options must be a property list!"); + + if (elements[0] == atom_encrypt) + { + if (*encflgp != -14) + return EXCP_BADARG_N(env, opts_arg_num, "'encrypt' option is present more than once!"); + else if (elements[1] == atom_true) + *encflgp = 1; + else if (elements[1] == atom_false) + *encflgp = 0; + else if (elements[1] == atom_undefined) + *encflgp = -1; /* For compat funcs in crypto.erl. TODO: check and remove */ + else + return EXCP_BADARG_N(env, opts_arg_num, "The 'encrypt' option must be a boolean!"); + } + else if (elements[0] == atom_padding) + { + if (*padflgp != atom_false) + return EXCP_BADARG_N(env, opts_arg_num, "The 'padding' option is present more than once!"); + + else if ((elements[1] == atom_undefined) || + (elements[1] == atom_none) || + (elements[1] == atom_zero) || + (elements[1] == atom_random) || + (elements[1] == atom_pkcs_padding) + ) + *padflgp = elements[1]; + + else + return EXCP_BADARG_N(env, opts_arg_num, "Bad 'padding' option value"); + } + else + { + char msg[64]; + if (enif_snprintf(msg, 64, "Bad tag in option: %T", elements[0])) + return EXCP_BADARG_N(env, opts_arg_num, msg); + else + return EXCP_BADARG_N(env, opts_arg_num, "Bad tag in option!"); + } + + p = tl; /* prepare for handling next list element or to exit the loop */ + } + + if (*encflgp == -14) + *encflgp = 1; /* {encrypt,true} is the default */ + + if (*padflgp == atom_false) + *padflgp = atom_undefined; /* {padding,undefined} is the default */ + + return atom_ok; +} + + static int get_init_args(ErlNifEnv* env, struct evp_cipher_ctx *ctx_res, - const ERL_NIF_TERM cipher_arg, - const ERL_NIF_TERM key_arg, - const ERL_NIF_TERM ivec_arg, - const ERL_NIF_TERM encflg_arg, - const ERL_NIF_TERM padding_arg, + const ERL_NIF_TERM argv[], + int cipher_arg_num, + int key_arg_num, + int ivec_arg_num, + int opts_arg_num, const struct cipher_type_t **cipherp, ERL_NIF_TERM *return_term) { @@ -103,57 +207,48 @@ static int get_init_args(ErlNifEnv* env, #if !defined(HAVE_EVP_AES_CTR) ctx_res->env = NULL; /* For testing if *env should be freed after errors */ #endif - ctx_res->padding = atom_undefined; ctx_res->padded_size = -1; ctx_res->size = 0; - - /* Fetch the flag telling if we are going to encrypt (=true) or decrypt (=false) */ - if (encflg_arg == atom_true) - ctx_res->encflag = 1; - else if (encflg_arg == atom_false) - ctx_res->encflag = 0; - else if (encflg_arg == atom_undefined) - /* For compat funcs in crypto.erl */ - ctx_res->encflag = -1; - else - { - *return_term = EXCP_BADARG(env, "Bad enc flag"); - goto err; - } + + /* Fetch the options */ + if ((*return_term = + get_opts(env, argv[opts_arg_num], opts_arg_num, &(ctx_res->encflag), &(ctx_res->padding)) + ) != atom_ok) + goto err; /* Fetch the key */ - if (!enif_inspect_iolist_as_binary(env, key_arg, &key_bin)) + if (!enif_inspect_iolist_as_binary(env, argv[key_arg_num], &key_bin)) { - *return_term = EXCP_BADARG(env, "Bad key"); + *return_term = EXCP_BADARG_N(env, key_arg_num, "Bad key"); goto err; } /* Fetch cipher type */ - if (!enif_is_atom(env, cipher_arg)) + if (!enif_is_atom(env, argv[cipher_arg_num])) { - *return_term = EXCP_BADARG(env, "Cipher id is not an atom"); + *return_term = EXCP_BADARG_N(env, cipher_arg_num, "Cipher id is not an atom"); goto err; } - if (!(*cipherp = get_cipher_type(cipher_arg, key_bin.size))) + if (!(*cipherp = get_cipher_type(argv[cipher_arg_num], key_bin.size))) { - if (!get_cipher_type_no_key(cipher_arg)) - *return_term = EXCP_BADARG(env, "Unknown cipher"); + if (!get_cipher_type_no_key(argv[cipher_arg_num])) + *return_term = EXCP_BADARG_N(env, cipher_arg_num, "Unknown cipher"); else - *return_term = EXCP_BADARG(env, "Bad key size"); + *return_term = EXCP_BADARG_N(env, key_arg_num, "Bad key size"); goto err; } if ((*cipherp)->flags & AEAD_CIPHER) { - *return_term = EXCP_BADARG(env, "Missing arguments for this cipher"); + *return_term = EXCP_BADARG_N(env, cipher_arg_num, "Missing arguments for this cipher"); goto err; } if (CIPHER_FORBIDDEN_IN_FIPS(*cipherp)) { - *return_term = EXCP_NOTSUP(env, "Forbidden in FIPS"); + *return_term = EXCP_NOTSUP_N(env, cipher_arg_num, "Forbidden in FIPS"); goto err; } @@ -171,32 +266,34 @@ static int get_init_args(ErlNifEnv* env, ivec_len = 16; else { /* Unsupported crypto */ - *return_term = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version"); + *return_term = + EXCP_NOTSUP_N(env, cipher_arg_num, "Cipher not supported in this libcrypto version"); goto err; } } #else /* Normal code */ if (!((*cipherp)->cipher.p)) { - *return_term = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version"); + *return_term = + EXCP_NOTSUP_N(env, cipher_arg_num, "Cipher not supported in this libcrypto version"); goto err; } ivec_len = GET_IV_LEN(*cipherp); #endif - /* (*cipherp)->cipher.p != NULL and ivec_len has a value */ + /* Here: (*cipherp)->cipher.p != NULL and ivec_len has a value */ /* Fetch IV */ - if (ivec_len && (ivec_arg != atom_undefined)) { - if (!enif_inspect_iolist_as_binary(env, ivec_arg, &ivec_bin)) + if (ivec_len && (argv[ivec_arg_num] != atom_undefined)) { + if (!enif_inspect_iolist_as_binary(env, argv[ivec_arg_num], &ivec_bin)) { - *return_term = EXCP_BADARG(env, "Bad iv type"); + *return_term = EXCP_BADARG_N(env, ivec_arg_num, "Bad iv type"); goto err; } if (ivec_len != ivec_bin.size) { - *return_term = EXCP_BADARG(env, "Bad iv size"); + *return_term = EXCP_BADARG_N(env, ivec_arg_num, "Bad iv size"); goto err; } } @@ -211,7 +308,7 @@ static int get_init_args(ErlNifEnv* env, ERL_NIF_TERM ecount_bin; unsigned char *outp; if ((outp = enif_make_new_binary(env, AES_BLOCK_SIZE, &ecount_bin)) == NULL) { - *return_term = EXCP_ERROR(env, "Can't allocate ecount_bin"); + *return_term = EXCP_ERROR(env, "Can't allocate output data binary"); goto err; } memset(outp, 0, AES_BLOCK_SIZE); @@ -223,7 +320,7 @@ static int get_init_args(ErlNifEnv* env, } ctx_res->state = enif_make_copy(ctx_res->env, - enif_make_tuple4(env, key_arg, ivec_arg, ecount_bin, enif_make_int(env, 0))); + enif_make_tuple4(env, argv[key_arg_num], argv[ivec_arg_num], ecount_bin, enif_make_int(env, 0))); goto success; } else { /* Flag for subsequent calls that no aes_ctr compatibility code should be called */ @@ -249,27 +346,27 @@ static int get_init_args(ErlNifEnv* env, if (!EVP_CIPHER_CTX_set_key_length(ctx_res->ctx, (int)key_bin.size)) { - *return_term = EXCP_ERROR(env, "Can't initialize context, key_length"); + *return_term = EXCP_ERROR_N(env, key_arg_num, "Can't initialize context, key_length"); goto err; } #ifdef HAVE_RC2 if (EVP_CIPHER_type((*cipherp)->cipher.p) == NID_rc2_cbc) { if (key_bin.size > INT_MAX / 8) { - *return_term = EXCP_BADARG(env, "To large rc2_cbc key"); + *return_term = EXCP_BADARG_N(env, key_arg_num, "To large rc2_cbc key"); goto err; } if (!EVP_CIPHER_CTX_ctrl(ctx_res->ctx, EVP_CTRL_SET_RC2_KEY_BITS, (int)key_bin.size * 8, NULL)) { - *return_term = EXCP_ERROR(env, "ctrl rc2_cbc key"); + *return_term = EXCP_BADARG_N(env, key_arg_num, "ctrl rc2_cbc key"); goto err; } } #endif - if (ivec_arg == atom_undefined || ivec_len == 0) + if (argv[ivec_arg_num] == atom_undefined || ivec_len == 0) { if (!EVP_CipherInit_ex(ctx_res->ctx, NULL, NULL, key_bin.data, NULL, -1)) { - *return_term = EXCP_ERROR(env, "Can't initialize key"); + *return_term = EXCP_BADARG_N(env, key_arg_num, "Can't initialize key"); goto err; } } @@ -281,20 +378,9 @@ static int get_init_args(ErlNifEnv* env, } /* Set padding */ - if ((padding_arg == atom_undefined) || - (padding_arg == atom_none) || - (padding_arg == atom_zero) || - (padding_arg == atom_random) ) + if (ctx_res->padding != atom_pkcs_padding) /* pkcs_padding is default */ EVP_CIPHER_CTX_set_padding(ctx_res->ctx, 0); - else if (padding_arg != atom_pkcs_padding) /* pkcs_padding is default */ - { - *return_term = EXCP_BADARG(env, "Bad padding flag"); - goto err; - } - - ctx_res->padding = padding_arg; - *return_term = atom_ok; #if !defined(HAVE_EVP_AES_CTR) @@ -309,18 +395,18 @@ static int get_init_args(ErlNifEnv* env, /*************************************************************************/ /* Get the arguments for the EVP_CipherUpdate function, and call it. */ /*************************************************************************/ - static int get_update_args(ErlNifEnv* env, struct evp_cipher_ctx *ctx_res, - const ERL_NIF_TERM indata_arg, + const ERL_NIF_TERM argv[], + int indata_arg_num, ERL_NIF_TERM *return_term) { ErlNifBinary in_data_bin, out_data_bin; int out_len, block_size; - if (!enif_inspect_binary(env, indata_arg, &in_data_bin) ) + if (!enif_inspect_iolist_as_binary(env, argv[indata_arg_num], &in_data_bin) ) { - *return_term = EXCP_BADARG(env, "Bad 2:nd arg"); + *return_term = EXCP_BADARG_N(env, indata_arg_num, "Expected binary"); goto err0; } @@ -340,7 +426,7 @@ static int get_update_args(ErlNifEnv* env, if (enif_get_tuple(env, state0, &tuple_argc, &tuple_argv) && (tuple_argc == 4)) { /* A compatibility state term */ /* encrypt and decrypt is performed by calling the same function */ - newstate_and_outdata = aes_ctr_stream_encrypt_compat(env, state0, indata_arg); + newstate_and_outdata = aes_ctr_stream_encrypt_compat(env, state0, argv[indata_arg_num]); if (enif_get_tuple(env, newstate_and_outdata, &tuple_argc, &tuple_argv) && (tuple_argc == 2)) { /* newstate_and_outdata = {NewState, OutData} */ @@ -488,7 +574,11 @@ static int get_final_args(ErlNifEnv* env, else { - *return_term = EXCP_ERROR(env, "Bad padding flg"); + char msg[64]; + if (enif_snprintf(msg, 64, "Bad padding flag: %T", ctx_res->padding)) + *return_term = EXCP_ERROR(env, msg); + else + *return_term = EXCP_ERROR(env, "Bad padding flg"); goto err; } @@ -567,7 +657,7 @@ static int get_final_args(ErlNifEnv* env, /*************************************************************************/ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Cipher, Key, IVec, Encrypt, Padding) % if no IV for the Cipher, set IVec = <<>> +{/* (Cipher, Key, IVec, OptionsMap) % if no IV for the Cipher, set IVec = <<>> */ struct evp_cipher_ctx *ctx_res = NULL; const struct cipher_type_t *cipherp; @@ -577,8 +667,7 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg if ((ctx_res = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) return EXCP_ERROR(env, "Can't allocate resource"); - if (get_init_args(env, ctx_res, argv[0], argv[1], argv[2], argv[3], argv[4], - &cipherp, &ret)) + if (get_init_args(env, ctx_res, argv, 0, 1, 2, 3, &cipherp, &ret)) ret = enif_make_resource(env, ctx_res); /* else error msg in ret */ @@ -591,7 +680,7 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg else if (argv[3] == atom_false) ctx_res->encflag = 0; else { - ret = EXCP_BADARG(env, "Bad enc flag"); + ret = EXCP_BADARG_N(env, 3, "Expected true or false"); goto ret; } if (ctx_res->ctx) { @@ -603,7 +692,7 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg } ret = argv[0]; } else { - ret = EXCP_BADARG(env, "Bad 1:st arg"); + ret = EXCP_BADARG_N(env, 0, "Expected cipher name atom"); goto ret; } @@ -625,7 +714,7 @@ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ ctx_res_copy.ctx = NULL; if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res)) - return EXCP_BADARG(env, "Bad 1:st arg"); + return EXCP_BADARG_N(env, 0, "Bad State"); if (argc == 3) { /* We have an IV in this call. Make a copy of the context */ @@ -647,13 +736,13 @@ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ if (!enif_inspect_iolist_as_binary(env, argv[2], &ivec_bin)) { - ret = EXCP_BADARG(env, "Bad iv type"); + ret = EXCP_BADARG_N(env, 2, "Bad iv type"); goto err; } if (ctx_res_copy.iv_len != ivec_bin.size) { - ret = EXCP_BADARG(env, "Bad iv size"); + ret = EXCP_BADARG_N(env, 2, "Bad iv size"); goto err; } @@ -676,11 +765,11 @@ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ goto err; } - get_update_args(env, &ctx_res_copy, argv[1], &ret); + get_update_args(env, &ctx_res_copy, argv, 1, &ret); ctx_res->size = ctx_res_copy.size; } else /* argc != 3, that is, argc = 2 (we don't have an IV in this call) */ - get_update_args(env, ctx_res, argv[1], &ret); + get_update_args(env, ctx_res, argv, 1, &ret); err: if (ctx_res_copy.ctx) @@ -694,11 +783,11 @@ ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a {/* (Context, Data [, IV]) */ ErlNifBinary data_bin; - if (!enif_inspect_binary(env, argv[1], &data_bin)) - return EXCP_BADARG(env, "expected binary as data"); + if (!enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) + return EXCP_BADARG_N(env, 1, "expected binary"); if (data_bin.size > INT_MAX) - return EXCP_BADARG(env, "to long data"); + return EXCP_BADARG_N(env, 1, "too long data"); /* Run long jobs on a dirty scheduler to not block the current emulator thread */ if (data_bin.size > MAX_BYTES_TO_NIF) { @@ -721,7 +810,7 @@ ERL_NIF_TERM ng_crypto_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar ERL_NIF_TERM ret; if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res)) - return EXCP_BADARG(env, "Bad arg"); + return EXCP_BADARG_N(env, 0, "Bad State"); get_final_args(env, ctx_res, &ret); @@ -733,7 +822,7 @@ ERL_NIF_TERM ng_crypto_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar /*************************************************************************/ ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Cipher, Key, IVec, Data, Encrypt, PaddingType) */ +{/* (Cipher, Key, IVec, Data, OptionsMap) */ struct evp_cipher_ctx ctx_res; const struct cipher_type_t *cipherp; ERL_NIF_TERM ret; @@ -746,11 +835,11 @@ ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg #endif /* EVP_CipherInit */ - if (!get_init_args(env, &ctx_res, argv[0], argv[1], argv[2], argv[4], argv[5], &cipherp, &ret)) + if (!get_init_args(env, &ctx_res, argv, 0, 1, 2, 4, &cipherp, &ret)) goto out; /* out_data = EVP_CipherUpdate */ - if (!get_update_args(env, &ctx_res, argv[3], &ret)) + if (!get_update_args(env, &ctx_res, argv, 3, &ret)) /* Got an exception as result in &ret */ goto out; @@ -800,11 +889,11 @@ ERL_NIF_TERM ng_crypto_one_time_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM */ ErlNifBinary data_bin; - if (!enif_inspect_binary(env, argv[3], &data_bin)) - return EXCP_BADARG(env, "expected binary as data"); + if (!enif_inspect_iolist_as_binary(env, argv[3], &data_bin)) + return EXCP_BADARG_N(env, 3, "expected binary as data"); if (data_bin.size > INT_MAX) - return EXCP_BADARG(env, "to long data"); + return EXCP_BADARG_N(env, 3, "too long data"); /* Run long jobs on a dirty scheduler to not block the current emulator thread */ if (data_bin.size > MAX_BYTES_TO_NIF) { @@ -827,7 +916,7 @@ ERL_NIF_TERM ng_crypto_get_data_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ERL_NIF_TERM ret; if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res)) - return EXCP_BADARG(env, "Bad arg"); + return EXCP_BADARG_N(env, 0, "Bad State"); ret = enif_make_new_map(env); diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c index 5671e6d801..cae3c5287d 100644 --- a/lib/crypto/c_src/atoms.c +++ b/lib/crypto/c_src/atoms.c @@ -24,6 +24,8 @@ ERL_NIF_TERM atom_true; ERL_NIF_TERM atom_false; ERL_NIF_TERM atom_sha; ERL_NIF_TERM atom_error; +ERL_NIF_TERM atom_encrypt; +ERL_NIF_TERM atom_padding; ERL_NIF_TERM atom_pkcs_padding; ERL_NIF_TERM atom_zero; ERL_NIF_TERM atom_random; @@ -165,6 +167,8 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM atom_sha = enif_make_atom(env,"sha"); atom_error = enif_make_atom(env,"error"); + atom_encrypt = enif_make_atom(env,"encrypt"); + atom_padding = enif_make_atom(env,"padding"); atom_pkcs_padding = enif_make_atom(env,"pkcs_padding"); atom_zero = enif_make_atom(env,"zero"); atom_random = enif_make_atom(env,"random"); diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h index 414e3045ea..d6bdc43a52 100644 --- a/lib/crypto/c_src/atoms.h +++ b/lib/crypto/c_src/atoms.h @@ -28,6 +28,8 @@ extern ERL_NIF_TERM atom_true; extern ERL_NIF_TERM atom_false; extern ERL_NIF_TERM atom_sha; extern ERL_NIF_TERM atom_error; +extern ERL_NIF_TERM atom_encrypt; +extern ERL_NIF_TERM atom_padding; extern ERL_NIF_TERM atom_pkcs_padding; extern ERL_NIF_TERM atom_zero; extern ERL_NIF_TERM atom_random; diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index 5e7fc88d72..284ea0ba81 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -317,10 +317,13 @@ const struct cipher_type_t* get_cipher_type_no_key(ERL_NIF_TERM type) int cmp_cipher_types_no_key(const void *keyp, const void *elemp) { const struct cipher_type_t *key = keyp; const struct cipher_type_t *elem = elemp; + int ret; - if (key->type.atom < elem->type.atom) return -1; - else if (key->type.atom > elem->type.atom) return 1; - else /* key->type.atom == elem->type.atom */ return 0; + if (key->type.atom < elem->type.atom) ret = -1; + else if (key->type.atom > elem->type.atom) ret = 1; + else /* key->type.atom == elem->type.atom */ ret = 0; + + return ret; } diff --git a/lib/crypto/c_src/common.c b/lib/crypto/c_src/common.c new file mode 100644 index 0000000000..e5160548c4 --- /dev/null +++ b/lib/crypto/c_src/common.c @@ -0,0 +1,53 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2021. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "common.h" + +ERL_NIF_TERM raise_exception(ErlNifEnv* env, ERL_NIF_TERM id, int arg_num, char* explanation, char* file, int line) +/* Ex: raise_exception(atom_badarg, 1, "Unknown cipher", "api_ng.c", 17) + * -> {badarg, {"api_ng.c",17}, 1, "Unknown cipher"} + * id = atom_error | atom_notsup | atom_badarg + * arg_num is the (zero-based) position in argv[]. -1 is to signal that it is undefined. + */ +{ + ERL_NIF_TERM file_info, exception; + + file_info = enif_make_new_map(env); + enif_make_map_put(env, file_info, + enif_make_atom(env,"c_file_name"), + enif_make_string(env, file, (ERL_NIF_LATIN1)), + &file_info); + enif_make_map_put(env, file_info, + enif_make_atom(env,"c_file_line_num"), + enif_make_int(env, line), + &file_info); + enif_make_map_put(env, file_info, + enif_make_atom(env,"c_function_arg_num"), + enif_make_int(env, arg_num), + &file_info); + exception = + enif_make_tuple3(env, + id, + file_info, + enif_make_string(env, explanation, (ERL_NIF_LATIN1)) + ); + + return enif_raise_exception(env, exception); +} diff --git a/lib/crypto/c_src/common.h b/lib/crypto/c_src/common.h index 22aa38d806..8406cd0b7c 100644 --- a/lib/crypto/c_src/common.h +++ b/lib/crypto/c_src/common.h @@ -37,16 +37,13 @@ /* All nif functions return a valid value or throws an exception */ -#define EXCP(Env, Id, Str) enif_raise_exception((Env), \ - enif_make_tuple3((Env), \ - (Id), \ - enif_make_tuple2((Env), \ - enif_make_string((Env),__FILE__,(ERL_NIF_LATIN1)), \ - enif_make_int((Env), __LINE__)), \ - enif_make_string((Env),(Str),(ERL_NIF_LATIN1)) )) - -#define EXCP_NOTSUP(Env, Str) EXCP((Env), atom_notsup, (Str)) -#define EXCP_BADARG(Env, Str) EXCP((Env), atom_badarg, (Str)) -#define EXCP_ERROR(Env, Str) EXCP((Env), atom_error, (Str)) +ERL_NIF_TERM raise_exception(ErlNifEnv* env, ERL_NIF_TERM id, int arg_num, char* explanation, char* file, int Line); + + +#define EXCP_ERROR(Env, Str) raise_exception((Env), atom_error, -1, (Str), __FILE__, __LINE__) +#define EXCP_NOTSUP(Env, Str) raise_exception((Env), atom_notsup, -1, (Str), __FILE__, __LINE__) +#define EXCP_ERROR_N(Env, ArgNum, Str) raise_exception((Env), atom_error, (ArgNum), (Str), __FILE__, __LINE__) +#define EXCP_NOTSUP_N(Env, ArgNum, Str) raise_exception((Env), atom_notsup, (ArgNum), (Str), __FILE__, __LINE__) +#define EXCP_BADARG_N(Env, ArgNum, Str) raise_exception((Env), atom_badarg, (ArgNum), (Str), __FILE__, __LINE__) #endif /* E_COMMON_H__ */ diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 66239462be..65804bf6fb 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -79,12 +79,12 @@ static ErlNifFunc nif_funcs[] = { {"mac_update_nif", 2, mac_update_nif, 0}, {"mac_final_nif", 1, mac_final_nif, 0}, {"cipher_info_nif", 1, cipher_info_nif, 0}, - {"ng_crypto_init_nif", 5, ng_crypto_init_nif, 0}, + {"ng_crypto_init_nif", 4, ng_crypto_init_nif, 0}, {"ng_crypto_update_nif", 2, ng_crypto_update_nif, 0}, {"ng_crypto_update_nif", 3, ng_crypto_update_nif, 0}, {"ng_crypto_final_nif", 1, ng_crypto_final_nif, 0}, {"ng_crypto_get_data_nif", 1, ng_crypto_get_data_nif, 0}, - {"ng_crypto_one_time_nif", 6, ng_crypto_one_time_nif, 0}, + {"ng_crypto_one_time_nif", 5, ng_crypto_one_time_nif, 0}, {"strong_rand_bytes_nif", 1, strong_rand_bytes_nif, 0}, {"strong_rand_range_nif", 1, strong_rand_range_nif, 0}, {"rand_uniform_nif", 2, rand_uniform_nif, 0}, @@ -131,7 +131,8 @@ ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload) static int verify_lib_version(void) { -#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) +#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) \ + || defined(HAS_LIBRESSL) const unsigned long libv = SSLeay(); #else const unsigned long libv = OpenSSL_version_num(); diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c index 8735158a01..13bd2815b1 100644 --- a/lib/crypto/c_src/mac.c +++ b/lib/crypto/c_src/mac.c @@ -176,10 +176,10 @@ ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifBinary text; if (!enif_inspect_iolist_as_binary(env, argv[3], &text)) - return EXCP_BADARG(env, "Bad text"); + return EXCP_BADARG_N(env, 3, "Bad text"); if (text.size > INT_MAX) - return EXCP_BADARG(env, "Too long text"); + return EXCP_BADARG_N(env, 3, "Too long text"); /* Run long jobs on a dirty scheduler to not block the current emulator thread */ if (text.size > MAX_BYTES_TO_NIF) { @@ -213,28 +213,28 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) */ if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) { - return_term = EXCP_BADARG(env, "Bad key"); + return_term = EXCP_BADARG_N(env, 2, "Bad key"); goto err; } if (!enif_inspect_iolist_as_binary(env, argv[3], &text)) { - return_term = EXCP_BADARG(env, "Bad text"); + return_term = EXCP_BADARG_N(env, 3, "Bad text"); goto err; } if (!(macp = get_mac_type(argv[0], key_bin.size))) { if (!get_mac_type_no_key(argv[0])) - return_term = EXCP_BADARG(env, "Unknown mac algorithm"); + return_term = EXCP_BADARG_N(env, 0, "Unknown mac algorithm"); else - return_term = EXCP_BADARG(env, "Bad key length"); + return_term = EXCP_BADARG_N(env, 2, "Bad key length"); goto err; } if (MAC_FORBIDDEN_IN_FIPS(macp)) { - return_term = EXCP_NOTSUP(env, "MAC algorithm forbidden in FIPS"); + return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS"); goto err; } @@ -256,17 +256,17 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if ((digp = get_digest_type(argv[1])) == NULL) { - return_term = EXCP_BADARG(env, "Bad digest algorithm for HMAC"); + return_term = EXCP_BADARG_N(env, 1, "Bad digest algorithm for HMAC"); goto err; } if (digp->md.p == NULL) { - return_term = EXCP_NOTSUP(env, "Unsupported digest algorithm"); + return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } if (DIGEST_FORBIDDEN_IN_FIPS(digp)) { - return_term = EXCP_NOTSUP(env, "Digest algorithm for HMAC forbidden in FIPS"); + return_term = EXCP_NOTSUP_N(env, 1, "Digest algorithm for HMAC forbidden in FIPS"); goto err; } md = digp->md.p; @@ -300,22 +300,22 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if (!(cipherp = get_cipher_type(argv[1], key_bin.size))) { /* Something went wrong. Find out what by retrying in another way. */ if (!get_cipher_type_no_key(argv[1])) - return_term = EXCP_BADARG(env, "Unknown cipher"); + return_term = EXCP_BADARG_N(env, 1, "Unknown cipher"); else /* Cipher exists, so it must be the key size that is wrong */ - return_term = EXCP_BADARG(env, "Bad key size"); + return_term = EXCP_BADARG_N(env, 2, "Bad key size"); goto err; } if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) { - return_term = EXCP_NOTSUP(env, "Cipher algorithm not supported in FIPS"); + return_term = EXCP_NOTSUP_N(env, 1, "Cipher algorithm not supported in FIPS"); goto err; } if (cipherp->cipher.p == NULL) { - return_term = EXCP_NOTSUP(env, "Unsupported cipher algorithm"); + return_term = EXCP_NOTSUP_N(env, 1, "Unsupported cipher algorithm"); goto err; } @@ -349,7 +349,7 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) case NO_mac: default: /* We know that this mac is supported with some version(s) of cryptolib */ - return_term = EXCP_NOTSUP(env, "Unsupported mac algorithm"); + return_term = EXCP_NOTSUP_N(env, 1, "Unsupported mac algorithm"); goto err; } @@ -506,22 +506,22 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) */ if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) { - return_term = EXCP_BADARG(env, "Bad key"); + return_term = EXCP_BADARG_N(env, 2, "Bad key"); goto err; } if (!(macp = get_mac_type(argv[0], key_bin.size))) { if (!get_mac_type_no_key(argv[0])) - return_term = EXCP_BADARG(env, "Unknown mac algorithm"); + return_term = EXCP_BADARG_N(env, 0, "Unknown mac algorithm"); else - return_term = EXCP_BADARG(env, "Bad key length"); + return_term = EXCP_BADARG_N(env, 2, "Bad key length"); goto err; } if (MAC_FORBIDDEN_IN_FIPS(macp)) { - return_term = EXCP_NOTSUP(env, "MAC algorithm forbidden in FIPS"); + return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS"); goto err; } @@ -543,17 +543,17 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if ((digp = get_digest_type(argv[1])) == NULL) { - return_term = EXCP_BADARG(env, "Bad digest algorithm for HMAC"); + return_term = EXCP_BADARG_N(env, 1, "Bad digest algorithm for HMAC"); goto err; } if (digp->md.p == NULL) { - return_term = EXCP_NOTSUP(env, "Unsupported digest algorithm"); + return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } if (DIGEST_FORBIDDEN_IN_FIPS(digp)) { - return_term = EXCP_NOTSUP(env, "Digest algorithm for HMAC forbidden in FIPS"); + return_term = EXCP_NOTSUP_N(env, 1, "Digest algorithm for HMAC forbidden in FIPS"); goto err; } md = digp->md.p; @@ -579,22 +579,22 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if (!(cipherp = get_cipher_type(argv[1], key_bin.size))) { /* Something went wrong. Find out what by retrying in another way. */ if (!get_cipher_type_no_key(argv[1])) - return_term = EXCP_BADARG(env, "Unknown cipher"); + return_term = EXCP_BADARG_N(env, 1, "Unknown cipher"); else /* Cipher exists, so it must be the key size that is wrong */ - return_term = EXCP_BADARG(env, "Bad key size"); + return_term = EXCP_BADARG_N(env, 2, "Bad key size"); goto err; } if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) { - return_term = EXCP_NOTSUP(env, "Cipher algorithm not supported in FIPS"); + return_term = EXCP_NOTSUP_N(env, 1, "Cipher algorithm not supported in FIPS"); goto err; } if (cipherp->cipher.p == NULL) { - return_term = EXCP_NOTSUP(env, "Unsupported cipher algorithm"); + return_term = EXCP_NOTSUP_N(env, 1, "Unsupported cipher algorithm"); goto err; } @@ -621,7 +621,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) case NO_mac: default: /* We know that this mac is supported with some version(s) of cryptolib */ - return_term = EXCP_NOTSUP(env, "Unsupported mac algorithm"); + return_term = EXCP_NOTSUP_N(env, 0, "Unsupported mac algorithm"); goto err; } @@ -666,7 +666,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #else if (argv[0] != atom_hmac) - return EXCP_NOTSUP(env, "Unsupported mac algorithm"); + return EXCP_NOTSUP_N(env, 0, "Unsupported mac algorithm"); return hmac_init_nif(env, argc, argv); #endif @@ -679,10 +679,10 @@ ERL_NIF_TERM mac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifBinary text; if (!enif_inspect_iolist_as_binary(env, argv[1], &text)) - return EXCP_BADARG(env, "Bad text"); + return EXCP_BADARG_N(env, 1, "Bad text"); if (text.size > INT_MAX) - return EXCP_BADARG(env, "Too long text"); + return EXCP_BADARG_N(env, 1, "Too long text"); /* Run long jobs on a dirty scheduler to not block the current emulator thread */ if (text.size > MAX_BYTES_TO_NIF) { @@ -702,10 +702,10 @@ ERL_NIF_TERM mac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifBinary text; if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj)) - return EXCP_BADARG(env, "Bad ref"); + return EXCP_BADARG_N(env, 0, "Bad ref"); if (!enif_inspect_iolist_as_binary(env, argv[1], &text)) - return EXCP_BADARG(env, "Bad text"); + return EXCP_BADARG_N(env, 1, "Bad text"); if (EVP_DigestSignUpdate(obj->ctx, text.data, text.size) != 1) return EXCP_ERROR(env, "EVP_DigestSignUpdate"); @@ -728,7 +728,7 @@ ERL_NIF_TERM mac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifBinary ret_bin; if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj)) - return EXCP_BADARG(env, "Bad ref"); + return EXCP_BADARG_N(env, 0, "Bad ref"); if (EVP_DigestSignFinal(obj->ctx, NULL, &size) != 1) return EXCP_ERROR(env, "Can't get sign size"); diff --git a/lib/crypto/c_src/otp_test_engine.c b/lib/crypto/c_src/otp_test_engine.c index f5fff85b14..cee251a280 100644 --- a/lib/crypto/c_src/otp_test_engine.c +++ b/lib/crypto/c_src/otp_test_engine.c @@ -59,6 +59,7 @@ /* If OPENSSL_NO_EC is set, there will be an error in ec.h included from engine.h So if EC is disabled, you can't use Engine either.... */ + #include <openssl/engine.h> #include <openssl/pem.h> @@ -192,7 +193,9 @@ static EVP_MD test_engine_md5_method= { EVP_PKEY_NULL_method, /* IGNORED: pkey methods */ MD5_CBLOCK, /* Internal blocksize, see rfc1321/md5.h */ sizeof(EVP_MD *) + sizeof(MD5_CTX), +# if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) NULL, /* IGNORED: control function */ +# endif }; #endif diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 5d72114055..473be885c9 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -42,7 +42,7 @@ -export([privkey_to_pubkey/2]). -export([ec_curve/1, ec_curves/0]). -export([rand_seed/1]). - +-export([format_error/2]). %%%---------------------------------------------------------------- %% Removed functions. %% @@ -403,23 +403,47 @@ %%-------------------------------------------------------------------- -%% -%% Make the new descriptive_error() look like the old run_time_error() -%% --define(COMPAT(CALL), - try begin CALL end - catch - error:{error, {_File,_Line}, _Reason} -> - error(badarg); - error:{E, {_File,_Line}, _Reason} when E==notsup ; E==badarg -> - error(E) - end). - +%% Compilation and loading %%-------------------------------------------------------------------- -compile(no_native). -on_load(on_load/0). -define(CRYPTO_NIF_VSN,302). +%%-------------------------------------------------------------------- +%% When generating documentation from crypto.erl, the macro ?CRYPTO_VSN is not defined. +%% That causes the doc generation to stop... +-ifndef(CRYPTO_VSN). +-define(CRYPTO_VSN, "??"). +-endif. + +%%-------------------------------------------------------------------- +%% Call a nif and handle an error exceptions to fit into the error handling +%% in the Erlang shell. +%% If not called from a shell, an error exception will be propagated. + +-define(nif_call(Call), ?nif_call(Call, undefined, {})). + +-define(nif_call(Call, ArgMap), ?nif_call(Call, undefined, ArgMap)). + +-define(nif_call(Call, Args0, ArgMap), + try Call + catch + error + : {Id, #{c_file_name := C_file, + c_file_line_num := C_line, + c_function_arg_num := ArgNum}, Msg} + : Stack when is_list(C_file), + is_integer(C_line), + is_integer(ArgNum) -> + error({Id, {C_file,C_line}, Msg}, + err_find_args(Args0, Stack), + [{error_info, #{erl_function_arg_num => err_remap_C_argnum(ArgNum, ArgMap)}}] + ) + end). + +%%-------------------------------------------------------------------- +%% Error if the crypto nifs not are loaded + -define(nif_stub,nif_stub_error(?LINE)). nif_stub_error(Line) -> erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}). @@ -427,16 +451,24 @@ nif_stub_error(Line) -> %%-------------------------------------------------------------------- %%% API %%-------------------------------------------------------------------- -%% Crypto app version history: -%% (no version): Driver implementation -%% 2.0 : NIF implementation, requires OTP R14 +version() -> + ?CRYPTO_VSN. + +format_error({Ex, {C_file,C_line}, Msg}, [{_M,_F,_Args,Opts} | _CallStack]) when Ex == badarg ; + Ex == notsup -> + case proplists:get_value(error_info, Opts) of + #{erl_function_arg_num := ErlArgNum} -> + FileMsg = + io_lib:format("(Found in the internal file ~s at line ~p)", [C_file, C_line]), + case ErlArgNum of + undefined -> + #{general => [Msg," ",FileMsg]}; + N ->#{N => Msg, + general => FileMsg + } + end + end. -%% When generating documentation from crypto.erl, the macro ?CRYPTO_VSN is not defined. -%% That causes the doc generation to stop... --ifndef(CRYPTO_VSN). --define(CRYPTO_VSN, "??"). --endif. -version() -> ?CRYPTO_VSN. -spec start() -> ok | {error, Reason::term()}. start() -> @@ -769,11 +801,9 @@ cipher_info(Type) -> FlagOrOptions :: crypto_opts() | boolean(), State :: crypto_state() . crypto_init(Cipher, Key, FlagOrOptions) -> - ng_crypto_init_nif(Cipher, - iolist_to_binary(Key), - <<>>, - get_crypto_opts(FlagOrOptions)). - + ?nif_call(ng_crypto_init_nif(alias(Cipher,Key), Key, <<>>, FlagOrOptions), + {1,2,-1,3} + ). -spec crypto_init(Cipher, Key, IV, FlagOrOptions) -> State | descriptive_error() when Cipher :: cipher_iv(), @@ -782,40 +812,7 @@ crypto_init(Cipher, Key, FlagOrOptions) -> FlagOrOptions :: crypto_opts(), State :: crypto_state() . crypto_init(Cipher, Key, IV, FlagOrOptions) -> - ng_crypto_init_nif(Cipher, - iolist_to_binary(Key), - iolist_to_binary(IV), - get_crypto_opts(FlagOrOptions)). - -%%%---------------------------------------------------------------- -get_crypto_opts(Options) when is_list(Options) -> - lists:foldl(fun chk_opt/2, - #{encrypt => true, - padding => undefined - }, - Options); -get_crypto_opts(Flag) when is_boolean(Flag) -> - #{encrypt => Flag, - padding => undefined - }; -get_crypto_opts(X) -> - error({badarg,{bad_option,X}}). - - -chk_opt({Tag,Val}, A) -> - case ok_opt(Tag,Val) of - true -> - A#{Tag => Val}; - false -> - error({badarg,{bad_option,{Tag,Val}}}) - end; -chk_opt(X, _) -> - error({badarg,{bad_option,X}}). - - -ok_opt(encrypt, V) -> lists:member(V, [true, false, undefined]); -ok_opt(padding, V) -> lists:member(V, [none, pkcs_padding, zero, random, undefined]); -ok_opt(_, _) -> false. + ?nif_call(ng_crypto_init_nif(alias(Cipher,Key), Key, IV, FlagOrOptions)). %%%---------------------------------------------------------------- -spec crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) -> State | descriptive_error() @@ -825,10 +822,10 @@ ok_opt(_, _) -> false. State :: crypto_state() . crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) -> %% The IV is supposed to be supplied by calling crypto_update/3 - ng_crypto_init_nif(Cipher, - iolist_to_binary(Key), - undefined, - get_crypto_opts(FlagOrOptions)). + ?nif_call(ng_crypto_init_nif(alias(Cipher,Key), Key, undefined, FlagOrOptions), + [Cipher, Key, FlagOrOptions], + {1,2,-1,3} + ). %%%---------------------------------------------------------------- %%% @@ -842,7 +839,7 @@ crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) -> Data :: iodata(), Result :: binary() . crypto_update(State, Data) -> - ng_crypto_update_nif(State, iolist_to_binary(Data)). + ?nif_call(ng_crypto_update_nif(State, Data)). %%%---------------------------------------------------------------- -spec crypto_dyn_iv_update(State, Data, IV) -> Result | descriptive_error() @@ -851,7 +848,7 @@ crypto_update(State, Data) -> IV :: iodata(), Result :: binary() . crypto_dyn_iv_update(State, Data, IV) -> - ng_crypto_update_nif(State, iolist_to_binary(Data), iolist_to_binary(IV)). + ?nif_call(ng_crypto_update_nif(State, Data, IV)). %%%---------------------------------------------------------------- %%% @@ -863,7 +860,7 @@ crypto_dyn_iv_update(State, Data, IV) -> when State :: crypto_state(), FinalResult :: binary() . crypto_final(State) -> - ng_crypto_final_nif(State). + ?nif_call(ng_crypto_final_nif(State)). %%%---------------------------------------------------------------- %%% @@ -873,7 +870,7 @@ crypto_final(State) -> when State :: crypto_state(), Result :: map() . crypto_get_data(State) -> - ng_crypto_get_data_nif(State). + ?nif_call(ng_crypto_get_data_nif(State)). %%%---------------------------------------------------------------- %%% @@ -890,11 +887,10 @@ crypto_get_data(State) -> Result :: binary() . crypto_one_time(Cipher, Key, Data, FlagOrOptions) -> - ng_crypto_one_time_nif(Cipher, - iolist_to_binary(Key), - <<>>, - iolist_to_binary(Data), - get_crypto_opts(FlagOrOptions)). + ?nif_call(ng_crypto_one_time_nif(alias(Cipher,Key), Key, <<>>, Data, FlagOrOptions), + [Cipher, Key, Data, FlagOrOptions], + {1,2,-1,3,4} + ). -spec crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) -> @@ -907,11 +903,9 @@ crypto_one_time(Cipher, Key, Data, FlagOrOptions) -> Result :: binary() . crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) -> - ng_crypto_one_time_nif(Cipher, - iolist_to_binary(Key), - iolist_to_binary(IV), - iolist_to_binary(Data), - get_crypto_opts(FlagOrOptions)). + ?nif_call(ng_crypto_one_time_nif(alias(Cipher,Key), Key, IV, Data, FlagOrOptions), + [Cipher, Key, IV, Data, FlagOrOptions], + {}). %%%---------------------------------------------------------------- -spec crypto_one_time_aead(Cipher, Key, IV, InText, AAD, EncFlag::true) -> @@ -927,7 +921,9 @@ crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) -> OutTag :: binary(). crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, true) -> - crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, aead_tag_len(Cipher), true). + ?nif_call(aead_cipher_nif(alias(Cipher,Key), Key, IV, PlainText, AAD, aead_tag_len(Cipher), true), + {1,2,3,4,5,-1,6} + ). -spec crypto_one_time_aead(Cipher, Key, IV, InText, AAD, TagOrTagLength, EncFlag) -> @@ -949,8 +945,10 @@ crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, true) -> OutPlainText :: binary(). crypto_one_time_aead(Cipher, Key, IV, TextIn, AAD, TagOrTagLength, EncFlg) -> - aead_cipher(Cipher, iolist_to_binary(Key), IV, - TextIn, AAD, TagOrTagLength, EncFlg). + ?nif_call(aead_cipher_nif(alias(Cipher,Key), Key, IV, TextIn, AAD, TagOrTagLength, EncFlg), + [Cipher, Key, IV, TextIn, AAD, TagOrTagLength, EncFlg], + {} + ). aead_tag_len(aes_ccm ) -> 12; @@ -968,12 +966,7 @@ aead_tag_len(_) -> %%%---------------------------------------------------------------- %%% Cipher NIFs -ng_crypto_init_nif(Cipher, Key, IVec, #{encrypt := EncryptFlag, - padding := Padding}) -> - ng_crypto_init_nif(alias(Cipher,Key), Key, IVec, EncryptFlag, Padding). - -ng_crypto_init_nif(_Cipher, _Key, _IVec, _EncryptFlag, _Padding) -> ?nif_stub. - +ng_crypto_init_nif(_Cipher, _Key, _IVec, _OptionsMap) -> ?nif_stub. ng_crypto_update_nif(_State, _Data) -> ?nif_stub. ng_crypto_update_nif(_State, _Data, _IV) -> ?nif_stub. @@ -982,15 +975,7 @@ ng_crypto_final_nif(_State) -> ?nif_stub. ng_crypto_get_data_nif(_State) -> ?nif_stub. -ng_crypto_one_time_nif(Cipher, Key, IVec, Data, #{encrypt := EncryptFlag, - padding := Padding}) -> - ng_crypto_one_time_nif(alias(Cipher,Key), Key, IVec, Data, EncryptFlag, Padding). - -ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _EncryptFlag, _Padding) -> ?nif_stub. - -%% The default tag length is EVP_GCM_TLS_TAG_LEN(16), -aead_cipher(Cipher, Key, IVec, AAD, In, TagOrLength, EncFlg) -> - aead_cipher_nif(alias(Cipher,Key), Key, IVec, AAD, In, TagOrLength, EncFlg). +ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _OptionsMap) -> ?nif_stub. aead_cipher_nif(_Type, _Key, _Ivec, _AAD, _In, _TagOrTagLength, _EncFlg) -> ?nif_stub. @@ -1000,13 +985,13 @@ cipher_info_nif(_Type) -> ?nif_stub. %%% Cipher aliases %%% -alias(aes_cbc, Key) -> alias1(aes_cbc, size(Key)); -alias(aes_cfb8, Key) -> alias1(aes_cfb8, size(Key)); -alias(aes_cfb128, Key) -> alias1(aes_cfb128, size(Key)); -alias(aes_ctr, Key) -> alias1(aes_ctr, size(Key)); -alias(aes_ecb, Key) -> alias1(aes_ecb, size(Key)); -alias(aes_gcm, Key) -> alias1(aes_gcm, size(Key)); -alias(aes_ccm, Key) -> alias1(aes_ccm, size(Key)); +alias(aes_cbc, Key) -> alias1(aes_cbc, iolist_size(Key)); +alias(aes_cfb8, Key) -> alias1(aes_cfb8, iolist_size(Key)); +alias(aes_cfb128, Key) -> alias1(aes_cfb128, iolist_size(Key)); +alias(aes_ctr, Key) -> alias1(aes_ctr, iolist_size(Key)); +alias(aes_ecb, Key) -> alias1(aes_ecb, iolist_size(Key)); +alias(aes_gcm, Key) -> alias1(aes_gcm, iolist_size(Key)); +alias(aes_ccm, Key) -> alias1(aes_ccm, iolist_size(Key)); alias(Alg, _) -> Alg. @@ -2511,3 +2496,24 @@ choose_otp_test_engine([LibName | T], Type, Acc) -> end; choose_otp_test_engine([], _, Acc) -> Acc. + +%%%---------------------------------------------------------------- +%%% Error internals + +err_find_args(undefined, [{?MODULE,_F,Args,_Info}|_]) -> Args; +err_find_args(Args, _) -> Args. + + +err_remap_C_argnum(ArgNum, ArgMap) -> + try + element(ArgNum + 1, ArgMap) % 0-numbered in C-file's argv[], 1-numbered in the tuple + of + N when is_integer(N), N>0 -> N; + _ -> undefined + catch + error:badarg when ArgNum >= 0 -> ArgNum+1; % short ArgMap + error:badarg -> undefined + end. + + +%%%---------------------------------------------------------------- diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 499a4928de..2f91f7037a 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -988,22 +988,6 @@ expected_ec_size({{prime_field,_}, _, _, Order, _}) -> byte_size(Order); expected_ec_size({{characteristic_two_field, _, _}, _, _, Order, _}) -> size(Order). %%-------------------------------------------------------------------- -no_aead() -> - [{doc, "Test disabled aead ciphers"}]. -no_aead(Config) when is_list(Config) -> - EncArg4 = - case lazy_eval(proplists:get_value(cipher, Config)) of - [{Type, Key, PlainText, Nonce, AAD, CipherText, CipherTag, TagLen, _Info} | _] -> - {AAD, PlainText, TagLen}; - [{Type, Key, PlainText, Nonce, AAD, CipherText, CipherTag, _Info} | _] -> - {AAD, PlainText} - end, - EncryptArgs = [Type, Key, Nonce, EncArg4], - DecryptArgs = [Type, Key, Nonce, {AAD, CipherText, CipherTag}], - notsup(fun crypto:block_encrypt/4, EncryptArgs), - notsup(fun crypto:block_decrypt/4, DecryptArgs). - -%%-------------------------------------------------------------------- no_aead_ng() -> [{doc, "Test disabled aead ciphers"}]. no_aead_ng(Config) when is_list(Config) -> diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl index bac30ba51a..e75485f2bf 100644 --- a/lib/crypto/test/engine_SUITE.erl +++ b/lib/crypto/test/engine_SUITE.erl @@ -174,7 +174,7 @@ init_per_group(engine_fakes_rsa, Config) -> #{link_type := static} -> ct:log("~p:~p Statically linked",[?MODULE,?LINE]), {skip, "Statically linked"}; - Info -> + _Info -> %% Dynamically linked; use fake engine rsa implementation group_load_engine(Config, []) end; diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl index 2fc0767a01..32dcb25544 100644 --- a/lib/dialyzer/src/dialyzer.erl +++ b/lib/dialyzer/src/dialyzer.erl @@ -725,14 +725,14 @@ pp_type(Type) -> Form = {attribute, erl_anno:new(0), type, {t, Type, []}}, TypeDef = erl_pp:form(Form, [{quote_singleton_atom_types, true}]), {match, [S]} = re:run(TypeDef, <<"::\\s*(.*)\\.\\n*">>, - [{capture, all_but_first, list}, dotall]), + [{capture, all_but_first, list}, dotall, unicode]), S. pp_spec(Spec) -> Form = {attribute, erl_anno:new(0), spec, {{a,b,0}, Spec}}, Sig = erl_pp:form(Form, [{quote_singleton_atom_types, true}]), {match, [S]} = re:run(Sig, <<"-spec a:b\\s*(.*)\\.\\n*">>, - [{capture, all_but_first, list}, dotall]), + [{capture, all_but_first, list}, dotall, unicode]), S. parse_types_and_literals(Src) -> diff --git a/lib/dialyzer/test/dialyzer_common.erl b/lib/dialyzer/test/dialyzer_common.erl index beaf126344..392c41306e 100644 --- a/lib/dialyzer/test/dialyzer_common.erl +++ b/lib/dialyzer/test/dialyzer_common.erl @@ -154,7 +154,7 @@ check(TestCase, Opts, Dir, OutDir) -> _ -> case file:open(NewResFile,[?output_file_mode]) of {ok, OutFile} -> - io:format(OutFile,"\n~s",[Warns]), + file:write(OutFile, [$\n, unicode:characters_to_binary(Warns)]), file:close(OutFile); Other -> erlang:error(Other) end diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/unicode b/lib/dialyzer/test/opaque_SUITE_data/results/unicode new file mode 100644 index 0000000000..9cdab6d60d --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/results/unicode @@ -0,0 +1,2 @@ + +exposer.erl:5:26: The call problematic:'🔍'('bug') does not have an opaque term of type problematic:'🐛'() as 1st argument diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/unicode/exposer.erl b/lib/dialyzer/test/opaque_SUITE_data/src/unicode/exposer.erl new file mode 100644 index 0000000000..618f053532 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/unicode/exposer.erl @@ -0,0 +1,5 @@ +-module(exposer). + +-export(['💣'/0]). + +'💣'() -> problematic:'🔍'(bug). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/unicode/problematic.erl b/lib/dialyzer/test/opaque_SUITE_data/src/unicode/problematic.erl new file mode 100644 index 0000000000..c841b1a511 --- /dev/null +++ b/lib/dialyzer/test/opaque_SUITE_data/src/unicode/problematic.erl @@ -0,0 +1,9 @@ +-module(problematic). + +-opaque '🐛'() :: bug. +-export_type(['🐛'/0]). + +-export(['🔍'/1]). + +-spec '🔍'('🐛'()) -> true. +'🔍'(bug) -> true. diff --git a/lib/erl_docgen/priv/css/otp_doc.css b/lib/erl_docgen/priv/css/otp_doc.css index 915782ec8c..7a45a3a4f7 100644 --- a/lib/erl_docgen/priv/css/otp_doc.css +++ b/lib/erl_docgen/priv/css/otp_doc.css @@ -1,24 +1,26 @@ /* standard OTP style sheet */ +/* We use three responsive breakpoints: + * 768px - mobile + * 992px - small screen + * 1200px - medium screen + * default - large screen +*/ body { - background: #fefefe; - color: #1a1a1a; - font-family: sans-serif; - margin: 0; - padding: 0; - border: 0; - overflow: scroll; - height: 100%; - max-height: 100%; - line-height: 1.2em; - font-size: 16px; + background: #fefefe; + color: #1a1a1a; + font-family: sans-serif; + margin: 0; + padding: 0; + border: 0; + max-height: 100%; + line-height: 1.2em; + font-size: 16px; } h1, h2, h3, h4, h5, h6{ - line-height: 1.2em; + line-height: 1.2em; } -p { max-width: 42em } - .header { background: #222; color: #fefefe } .top { background: #efe } .otp { background: #efe } @@ -31,143 +33,310 @@ a:active { color: #1c7cd6; text-decoration: none } a:visited { color: #1b6ec2; text-decoration: none } #container { - width: 100%; - margin: 0; - background-color: #fefefe; + display: grid; + grid-template-columns: 1fr 5fr; + grid-template-areas: "leftnav content"; } #leftnav { - position: fixed; - float: left; - top: 0; - bottom: 0; - left: 0; - width: 300px; - overflow:auto; - margin: 0; - padding: 1px; - border-right: 1px solid #ccc; + grid-area: leftnav; } -.leftnav-tube -{ - margin: 15px; +.leftnav-tube { + top: 0; + margin-left: 15px; + position: sticky; + height: 100vh; + max-width: 250px; + overflow: auto; + border-right: 1px solid #ccc; } #content { - margin-left: 340px; /* set left value to WidthOfFrameDiv */ - max-width: 52em; - overflow-x: hidden; + grid-area: content; + max-width: 52em; + margin-left: 30px; +} + +@media screen and (max-width: 1200px) { + #content { + margin-left: 0px; + } +} + +@media screen and (max-width: 992px) { + #content { + max-width: 35em; + } +} + +.topbar { + grid-area: topbar; + display: none; +} + +@media screen and (max-width: 768px) { + + #container { + grid-template-columns: 100%; + grid-template-areas: + "topbar" + "leftnav" + "content"; + } + + /* styling for the top bar */ + .topbar { + display: flex; + position: relative; + top: 0px; + padding: 0.5em; + width: 100%; + box-sizing: border-box; + border-bottom: 1px solid #ccc; + box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.05), + inset 0 -1px 0 rgba(0,0,0,0.15); + text-align: center; + } + .topbar button { + padding: 1 6 1 6; + margin: 0; + height: auto; + display: inline-block; + border-radius: .25rem; + border: 1px solid transparent; + background: transparent; + } + .topbar-expand:not(.show) > button { + /* When we add the "show" class we rotate the button 180deg */ + transform: rotate(180deg); + } + .topbar-expand > button { + transition: transform 0.35s ease; + } + .topbar-expand.show-permanent > button { + display: none; + } + .topbar-title { + text-align: center; + flex-grow: 2; + } + .topbar-title > h1 { + font-size: 1.17em; + margin: 0px; + } + + /* Break words on mobile */ + #content { + overflow-wrap: break-word; + word-wrap: break-word; + margin-left: 0px; + max-width: 100%; + } + p { + max-width: 100%; + } + /* The h1 is part of the topbar on mobile */ + #content h1 { + display: none; + } + + /* Styling of the navbar when shown/hidden */ + #leftnav { + overflow: hidden; + } + #leftnav:not(.show) { + max-height: 0px; + border-bottom: 0px; + transition: max-height 0.35s ease; + } + #leftnav.show { + max-height: 60vh; + border-bottom: 1px solid #ccc; + box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.05), + inset 0 -1px 0 rgba(0,0,0,0.15); + transition: max-height 0.35s ease; + } + + .leftnav-tube { + max-height: 60vh; + max-width: 100%; + position: unset; + border-right: 0px; + margin-right: 0px; + margin-left: 0px; + padding: 0px 15px 0px 15px; + } + #leftnav.show-permanent { + max-height: none; + } + .show-permanent > .leftnav-tube { + overflow: initial; + max-height: none; + height: inherit; + } + .hide-mobile { + opacity: 0; + } + +} + +@media (prefers-reduced-motion: reduce){ + #leftnav.collapsing{ + transition: none; + } + .topbar-expand > button { + transition: none; + } +} + +.frontpage { + padding-top: 50px; /* Magins for inner DIV inside each DIV (to provide padding) */ +} + +.mobile-only { + display: none; +} + +@media screen and (max-width: 768px) { + .mobile-only { + display: revert; + } +} + +.innertube, .exports-tube { + margin-left: 15px; + margin-right: 15px; +} + +@media screen and (max-width: 1200px) { + .innertube, .exports-tube { + margin-left: 0px; + margin-right: 15px; + } +} + +@media screen and (max-width: 768px) { + .innertube, .exports-tube { + margin-left: 15px; + margin-right: 15px; + } +} + +.footer { + margin: 15px; /* Magins for inner DIV inside each DIV (to provide padding) */ + text-align: center; } -.frontpage -{ - padding-top: 50px; /* Magins for inner DIV inside each DIV (to provide padding) */ -} +.bold_code { font-family: mono, Courier, monospace; font-weight: bold } -.innertube -{ - margin-left: 15px; /* Magins for inner DIV inside each DIV (to provide padding) */ - margin-right: 11em; +.title-link { + display: flex; } -.footer -{ - margin: 15px; /* Magins for inner DIV inside each DIV (to provide padding) */ - text-align: center; +.cref-head, .func-head, .data-type-name { + margin: 0 0 0 -4em; + padding-bottom: 0.2em; + padding-top: 0.2em; } -.bold_code { font-family: mono, Courier, monospace; font-weight: bold } - -/* Invisible table for function specs, - * just to get since-version out in right margin */ -.func-table, .func-tr, .func-td, .cfunc-td, .func-since-td { - width: 100%; - border: 0; - padding: 0; +.cref-head h4, .func-head h4, .data-type-name h4 { margin: 0; } -.func-tr:nth-child(n) { - background: inherit /* turn off zebra striped rows */ -} - -.func-td { - width: 38em; +.cref-head:hover, .func-head:hover, .data-type-name:hover { + background-color: #f5f5f5; } -.cfunc-td { - width: 31em; +/* This small trick is in order to make all but the first line of the function + * head indented by 7em. So that it looks like this: + * int enif_alloc_binary( + * size_t size, + * ErlNifBinary* bin) + */ +.cref-head .title-name { padding-left: 7em; text-indent: -7em; } -.func-since-td { - width: auto; - padding-left: 1em; +.title-since { + margin-left: auto; } -.func-td:hover { - background-color: #f5f5f5; +@media screen and (max-width: 768px) { + .cref-head, .func-head, .data-type-name { + margin-left: 0em; + padding-bottom: 0em; + padding-top: 0em; + } + .title-link { + flex-direction: column; + } + .title-anchors { + display: none; + } + .title-since { + margin-left: 1em; + } } .code { - font-family: mono, Courier, monospace; - font-weight: normal; - background-color: #f3f3f3; + font-family: mono, Courier, monospace; + font-weight: normal; + background-color: #f3f3f3; } .note, .warning, .do, .dont { - border: 1px solid #495057; - margin: 1em 0; + border: 1px solid #495057; + margin: 1em 0; } .note .label { - background-color: #2b8a3e; - color: #fefefe; - font-weight: bold; - padding: 0.5em 1em; + background-color: #2b8a3e; + color: #fefefe; + font-weight: bold; + padding: 0.5em 1em; } .note .content { - background: #f8f9fa; - line-height: 120%; - font-size: 0.9em; - padding: 0.5em 1em; + background: #f8f9fa; + line-height: 120%; + font-size: 0.9em; + padding: 0.5em 1em; } .warning .label { - background: #c92a2a; - color: #fefefe; - font-weight: bold; - padding: 0.5em 1em; + background: #c92a2a; + color: #fefefe; + font-weight: bold; + padding: 0.5em 1em; } .warning .content { - background-color: #f8f9fa; - line-height: 120%; - font-size: 0.9em; - padding: 0.5em 1em; + background-color: #f8f9fa; + line-height: 120%; + font-size: 0.9em; + padding: 0.5em 1em; } .do .label { - background-color: #2b8a3e; - color: #fefefe; - font-weight: bold; - padding: 0.5em 1em; + background-color: #2b8a3e; + color: #fefefe; + font-weight: bold; + padding: 0.5em 1em; } .do .content { - background: #f8f9fa; - line-height: 120%; - font-size: 0.9em; - padding: 0.5em 1em; + background: #f8f9fa; + line-height: 120%; + font-size: 0.9em; + padding: 0.5em 1em; } .dont .label { - background: #c92a2a; - color: #fefefe; - font-weight: bold; - padding: 0.5em 1em; + background: #c92a2a; + color: #fefefe; + font-weight: bold; + padding: 0.5em 1em; } .dont .content { - background-color: #f8f9fa; - line-height: 120%; - font-size: 0.9em; - padding: 0.5em 1em; + background-color: #f8f9fa; + line-height: 120%; + font-size: 0.9em; + padding: 0.5em 1em; } .quote { @@ -175,61 +344,70 @@ a:visited { color: #1b6ec2; text-decoration: none } } .example { - background-color:#f1f3f5; - border: 1px solid #dee2e6; - padding: 0.5em 1em; - margin: 1em 0; - font-size: 0.7em; + background-color:#f1f3f5; + border: 1px solid #dee2e6; + padding: 0.5em 1em; + margin: 1em 0; + font-size: 0.7em; + overflow-x: auto; + max-width: min(100%, 48em); } .extrafrontpageinfo { - color: #C00; - font-weight: bold; - font-size: 1.2em; + color: #C00; + font-weight: bold; + font-size: 1.2em; } pre { - font-family: mono, Courier, monospace; - font-weight: normal; - margin: 0; + font-family: mono, Courier, monospace; + font-weight: normal; + margin: 0; } -.data-types-body, .REFBODY{ - margin-left: 2em; +.data-types-body, .REFBODY { + margin-left: 2em; } .REFTYPES { margin-left: 1.5em } .exports-body { margin-left: 3em; } -.exports-tube -{ - margin-right: 11em; + +@media screen and (max-width: 768px) { + .data-types-body, .REFBODY{ + margin-left: 1em; + } + .REFTYPES { margin-left: 0.75em } + .exports-body { + margin-left: 1em; + } } footer { } .erlang-logo-wrapper{ - text-align: center; - margin-bottom: 1em; + text-align: center; + margin-top: 1em; + margin-bottom: 1em; } .main-title{ - text-align: center; + text-align: center; } .main-description{ - text-align: center; - margin: 2em 0; - font-size: 1.5em; - line-height: 1.5em; + text-align: center; + margin: 2em 0; + font-size: 1.5em; + line-height: 1.5em; } .doc-table-wrapper, .doc-image-wrapper{ - width: 100%; + width: 100%; } .doc-image-wrapper{ - text-align: center; + text-align: center; } .doc-svg { @@ -237,60 +415,60 @@ footer { } } .doc-table, .doc-image{ - min-width: 50%; - margin: 0 auto; - font-size: 0.7em; + min-width: 50%; + margin: 0 auto; + font-size: 0.7em; } .doc-table-caption, .doc-image-caption{ - margin-top: 1em; - font-style: italic; - text-align: center; + margin-top: 1em; + font-style: italic; + text-align: center; } table { - border-collapse: collapse; - min-width: 50%; - margin: 1em; + border-collapse: collapse; + min-width: 50%; + margin: 1em; } table, th, td { - border: 1px solid #666; + border: 1px solid #666; } th, td { - padding: 0.5em; - text-align: left; + padding: 0.5em; + text-align: left; } tr:hover { - background-color: #f5f5f5; + background-color: #f5f5f5; } tr:nth-child(even) { - background-color: #f2f2f2; + background-color: #f2f2f2; } th { - background-color: #777; - color: #fefefe; + background-color: #777; + color: #fefefe; } .section-title, .section-subtitle, .section-version{ - text-align: center; - margin: 0; + text-align: center; + margin: 0; } .section-title{ - font-weight: bold; + font-weight: bold; } .section-version{ - font-size: small; + font-size: small; } .expand-collapse-items{ - font-size: small; + font-size: small; } .title_link { @@ -299,7 +477,6 @@ th { } .ghlink-before { - margin-left: -4em; visibility: hidden; } @@ -340,24 +517,24 @@ th { } hr{ - border: 0; - border-top: 1px solid #aaa; + border: 0; + border-top: 1px solid #aaa; } .section-links, .panel-sections, .expand-collapse-items{ - padding: 0 1em; + padding: 0 1em; } .section-links, .panel-sections{ - margin-top: 0; + margin-top: 0; } a > .code { - color: #1862ab; + color: #1862ab; } .func-types-title{ - font-size: 1em; + font-size: 1em; } .since{ diff --git a/lib/erl_docgen/priv/js/flipmenu/Makefile b/lib/erl_docgen/priv/js/flipmenu/Makefile index be0bed74fb..33cce4e6d6 100644 --- a/lib/erl_docgen/priv/js/flipmenu/Makefile +++ b/lib/erl_docgen/priv/js/flipmenu/Makefile @@ -76,7 +76,7 @@ release_spec: opt release_html_spec: html $(INSTALL_DIR) "$(RELEASE_PATH)/doc/js/flipmenu" $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELEASE_PATH)/doc/js/flipmenu" - $(INSTALL_DATA) ../highlight.js ../highlight.pack.js "$(RELEASE_PATH)/doc/js/" + $(INSTALL_DATA) ../highlight.js ../highlight.pack.js ../topbar.js "$(RELEASE_PATH)/doc/js/" release_docs_spec: $(DOC_TARGETS:%=release_%_spec) diff --git a/lib/erl_docgen/priv/js/topbar.js b/lib/erl_docgen/priv/js/topbar.js new file mode 100644 index 0000000000..ef36f09c8c --- /dev/null +++ b/lib/erl_docgen/priv/js/topbar.js @@ -0,0 +1,16 @@ + +function toggleDisplay() { + var leftnav=document.getElementById('leftnav'); + var topbar=document.getElementsByClassName('topbar-expand')[0]; + if (leftnav.classList.contains('show')) { + leftnav.classList.remove('show'); + topbar.classList.remove('show'); + setTimeout(() => { + leftnav.classList.add('hide-mobile'); + }, 350); + } else { + leftnav.classList.add('show'); + topbar.classList.add('show'); + leftnav.classList.remove('hide-mobile'); + } +} diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index 655d1a61e8..730c5d1447 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -229,30 +229,22 @@ <xsl:variable name="curModule" select="ancestor::erlref/module"/> <xsl:variable name="mfas" select="key('mfa', - concat($curModule,':',$name,'/',$arity))"/> - <xsl:choose> - <xsl:when test="generate-id($mfas[1]) != generate-id(.)"> - <!-- Avoid duplicated anchors. See also menu.funcs. --> - </xsl:when> - <xsl:otherwise> - <a name="{$name}-{$arity}"></a> - </xsl:otherwise> - </xsl:choose> + concat($curModule,':',$name,'/',$arity))"/> <!-- Insert an anchor for "anchor" attribute --> <xsl:if test="string-length($anchor) > 0"> - <a name="{$anchor}"></a> + <a name="{$anchor}"></a> </xsl:if> <xsl:variable name="global_types" select="ancestor::erlref/datatypes"/> <xsl:variable name="local_types" select="../type[string-length(@name) > 0]"/> - <xsl:apply-templates select="$spec/contract/clause/head"> - <xsl:with-param name="ghlink" select="ancestor-or-self::*[@ghlink]/@ghlink"/> - <xsl:with-param name="local_types" select="$local_types"/> - <xsl:with-param name="global_types" select="$global_types"/> - <xsl:with-param name="since" select="$since"/> - </xsl:apply-templates> + <xsl:apply-templates select="$spec/contract/clause/head"> + <xsl:with-param name="ghlink" select="ancestor-or-self::*[@ghlink]/@ghlink"/> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + <xsl:with-param name="since" select="$since"/> + </xsl:apply-templates> </xsl:when> </xsl:choose> </xsl:template> @@ -264,31 +256,51 @@ <xsl:param name="since"/> <xsl:variable name="mfa" select="concat(concat(../../../name,'-'),../../../arity)"/> <xsl:variable name="id" select="concat(concat($mfa,'-'),generate-id(.))"/> - <table class="func-table"> - <tr class="func-tr"> - <td class="func-td"> - <div class="bold_code func-head" - onMouseOver="document.getElementById('ghlink-{$id}').style.visibility = 'visible';" - onMouseOut="document.getElementById('ghlink-{$id}').style.visibility = 'hidden';"> - <xsl:call-template name="ghlink"> + <h4 id="{$mfa}" class="bold_code title-link func-head" + onMouseOver="document.getElementById('ghlink-{$id}').style.visibility = 'visible';" + onMouseOut="document.getElementById('ghlink-{$id}').style.visibility = 'hidden';"> + <div class="title-anchors"> + <xsl:call-template name="ghlink"> <xsl:with-param name="mfa" select="$mfa"/> <xsl:with-param name="ghlink" select="$ghlink"/> <xsl:with-param name="id" select="$id"/> - </xsl:call-template> - <xsl:apply-templates mode="local_type"> + </xsl:call-template> + </div> + <xsl:variable name="name"> + <xsl:apply-templates mode="local_type"> <xsl:with-param name="local_types" select="$local_types"/> <xsl:with-param name="global_types" select="$global_types"/> - </xsl:apply-templates> - </div> - </td> - <td class="func-since-td"> + </xsl:apply-templates> + </xsl:variable> + <!-- We do not include the erlang: prefix in what is selected by + .title-name. This is so that webcrawlers can select the true + function name, i.e. decode_packet instead of erlang:decode_packet. + --> + <xsl:variable name="head" select="exsl:node-set($name)/text()[1]"/> + <xsl:variable name="tail" select="exsl:node-set($name)/node()[position() > 1]"/> + <xsl:choose> + <xsl:when test="substring($head,1,7) = 'erlang:'"> + <div> + <xsl:text>erlang:</xsl:text> + <span class="title-name"> + <xsl:value-of select="substring-after($head,':')"/> + <xsl:copy-of select="$tail"/> + </span> + </div> + </xsl:when> + <xsl:otherwise> + <span class="title-name"> + <xsl:copy-of select="$name"/> + </span> + </xsl:otherwise> + </xsl:choose> <xsl:if test="string-length($since) > 0"> - <span class="since"><xsl:value-of select="$since"/> - </span> + <div class="title-since"> + <span class="since"><xsl:value-of select="$since"/> + </span> + </div> </xsl:if> - </td> - </tr> - </table> + </h4> </xsl:template> <!-- The *last* <name name="..." arity=".."/> --> @@ -317,7 +329,7 @@ (there is no spec with more than one clause) --> <xsl:if test="count($clause/guard) > 0 or count($type) > 0"> <div class="REFBODY fun-types"> - <h3 class="func-types-title">Types</h3> + <h3 id="types" class="func-types-title">Types</h3> <xsl:choose> <xsl:when test="$output_subtypes"> @@ -479,17 +491,15 @@ <!-- Datatype Title, is the really needed? not used by anything --> <xsl:template match="datatype_title"> <xsl:variable name="title" select="."/> - <h4> - <xsl:call-template name="title_link"> - <xsl:with-param name="title"><xsl:apply-templates/></xsl:with-param> - <xsl:with-param name="link" select="$title"/> - </xsl:call-template> - </h4> + <xsl:call-template name="title_link"> + <xsl:with-param name="title"><xsl:apply-templates/></xsl:with-param> + <xsl:with-param name="link" select="$title"/> + </xsl:call-template> </xsl:template> <!-- Datatype --> <xsl:template match="datatype"> - <div class="data-types-body"> + <article class="data-types-body"> <xsl:choose> <xsl:when test="string-length(name/@name) > 0"> <xsl:variable name="apostrophe">'</xsl:variable> @@ -509,15 +519,19 @@ <xsl:with-param name="by" select="$slash_encoded" /> </xsl:call-template> </xsl:variable> - <div class="data-type-name" + <h4 id="{$id}" class="title-link data-type-name" onMouseOver="document.getElementById('ghlink-{$id}').style.visibility = 'visible';" onMouseOut="document.getElementById('ghlink-{$id}').style.visibility = 'hidden';"> - <xsl:call-template name="ghlink"> - <xsl:with-param name="mfa" select="$id"/> - <xsl:with-param name="id" select="$id"/> - </xsl:call-template> - <xsl:apply-templates select="name"/> - </div> + <div class="title-anchors"> + <xsl:call-template name="ghlink"> + <xsl:with-param name="mfa" select="$id"/> + <xsl:with-param name="id" select="$id"/> + </xsl:call-template> + </div> + <div class="title-name"> + <xsl:apply-templates select="name"/> + </div> + </h4> </xsl:when> <xsl:otherwise> <div class="data-type-name"> @@ -526,7 +540,7 @@ </xsl:otherwise> </xsl:choose> <div class="data-type-desc"><xsl:apply-templates select="desc"/></div> - </div> + </article> </xsl:template> <!-- The "mode" attribute of apply has been used to separate the case @@ -774,6 +788,8 @@ <xsl:param name="curModule"/> <html> <head> + <meta name="viewport" content="width=device-width, initial-scale=1"></meta> + <meta charset="utf-8"></meta> <xsl:choose> <xsl:when test="string-length($stylesheet) > 0"> <link rel="stylesheet" href="{$topdocdir}/{$stylesheet}" type="text/css"/> @@ -792,32 +808,18 @@ </xsl:choose> </head> <body> - <div id="container"> <script id="js" type="text/javascript" language="JavaScript" src="{$topdocdir}/js/flipmenu/flipmenu.js"/> <script id="js2" type="text/javascript" src="{$topdocdir}/js/erlresolvelinks.js"></script> + <script id="js3" type="text/javascript" src="{$topdocdir}/js/topbar.js"></script> <script language="JavaScript" type="text/javascript"> <xsl:text disable-output-escaping="yes"><![CDATA[ <!-- - function getWinHeight() { - var myHeight = 0; - if( typeof( window.innerHeight ) == 'number' ) { - //Non-IE - myHeight = window.innerHeight; - } else if( document.documentElement && ( document.documentElement.clientWidth || - document.documentElement.clientHeight ) ) { - //IE 6+ in 'standards compliant mode' - myHeight = document.documentElement.clientHeight; - } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { - //IE 4 compatible - myHeight = document.body.clientHeight; - } - return myHeight; - } - function setscrollpos() { - var objf=document.getElementById('loadscrollpos'); - document.getElementById("leftnav").scrollTop = objf.offsetTop - getWinHeight()/2; + var objf = document.getElementById('loadscrollpos'); + if (objf) { + document.getElementById("leftnav").firstChild.scrollTop = objf.offsetTop - 10; + } } function addEvent(obj, evType, fn){ @@ -834,8 +836,41 @@ addEvent(window, 'load', setscrollpos); - //-->]]></xsl:text> + //--> +]]></xsl:text> </script> + <div class="topbar"> + <xsl:variable name="show"> + <xsl:if test="(local-name() = 'application') or (local-name() = 'part') or (local-name() = 'releasenotes')"> + <!-- For index pages we want to always show the navbar for mobile --> + <xsl:text>show show-permanent</xsl:text> + </xsl:if> + </xsl:variable> + <div class="topbar-expand {$show}"> + <button onclick="toggleDisplay();"> + <svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" width="24" height="24"> + <g> + <path style="fill:#000000;" d="M27,54c-0.552,0-1-0.448-1-1V8c0-0.552,0.448-1,1-1s1,0.448,1,1v45C28,53.552,27.552,54,27,54z"/> + <path style="fill:#000000;" d="M11,25c-0.256,0-0.512-0.098-0.707-0.293c-0.391-0.391-0.391-1.023,0-1.414l16-16 + c0.391-0.391,1.023-0.391,1.414,0s0.391,1.023,0,1.414l-16,16C11.512,24.902,11.256,25,11,25z"/> + <path style="fill:#000000;" d="M43,25c-0.256,0-0.512-0.098-0.707-0.293l-16-16c-0.391-0.391-0.391-1.023,0-1.414 + s1.023-0.391,1.414,0l16,16c0.391,0.391,0.391,1.023,0,1.414C43.512,24.902,43.256,25,43,25z"/> + <path style="fill:#000000;" d="M43,2H11c-0.552,0-1-0.448-1-1s0.448-1,1-1h32c0.552,0,1,0.448,1,1S43.552,2,43,2z"/> + </g> + </svg> + </button> + </div> + <div class="topbar-title"> + <h1 id="{header/title}"> + <xsl:if test="string-length($chapnum) > 0"> + <xsl:value-of select="$chapnum"/>  + </xsl:if> + <xsl:value-of select="header/title"/> + </h1> + </div> + <div class="search-expand {$show}"> + </div> + </div> <!-- Generate menu --> <xsl:call-template name="menu"> <xsl:with-param name="chapnum" select="$chapnum"/> @@ -913,10 +948,22 @@ <xsl:template name="menu"> <xsl:param name="chapnum"/> <xsl:param name="curModule"/> + <xsl:variable name="show"> + <xsl:choose> + <xsl:when test="(local-name() = 'application') or (local-name() = 'part') or (local-name() = 'releasenotes')"> + <!-- For index pages we want to always show the navbar for mobile --> + <xsl:text>show show-permanent</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>hide-mobile</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> <xsl:if test="(local-name() = 'part') or ((local-name() = 'chapter') and ancestor::part)"> <!-- .../part or .../part/chapter --> <xsl:call-template name="menu.ug"> <xsl:with-param name="chapnum" select="$chapnum"/> + <xsl:with-param name="show" select="$show"/> </xsl:call-template> </xsl:if> @@ -930,12 +977,14 @@ <!-- .../internal or .../internal/chapter --> <xsl:call-template name="menu.internal.ug"> <xsl:with-param name="chapnum" select="$chapnum"/> + <xsl:with-param name="show" select="$show"/> </xsl:call-template> </xsl:when> <xsl:when test="(local-name() = 'internal' and descendant::erlref) or (((local-name() = 'erlref') or (local-name() = 'comref') or (local-name() = 'cref') or (local-name() = 'fileref') or (local-name() = 'appref')) and ancestor::internal)"> <!-- .../internal,.../internal/erlref, .../internal/comref or .../internal/cref or .../internal/fileref or .../internal/appref --> <xsl:call-template name="menu.internal.ref"> <xsl:with-param name="curModule" select="$curModule"/> + <xsl:with-param name="show" select="$show"/> </xsl:call-template> </xsl:when> </xsl:choose> @@ -943,12 +992,14 @@ <!-- .../application,.../application/erlref, .../application/comref or .../application/cref or .../application/fileref or .../application/appref --> <xsl:call-template name="menu.ref"> <xsl:with-param name="curModule" select="$curModule"/> + <xsl:with-param name="show" select="$show"/> </xsl:call-template> </xsl:if> <xsl:if test="(local-name() = 'releasenotes') or ((local-name() = 'chapter') and ancestor::releasenotes)"> <!-- releasenotes --> <xsl:call-template name="menu.rn"> <xsl:with-param name="chapnum" select="$chapnum"/> + <xsl:with-param name="show" select="$show"/> </xsl:call-template> </xsl:if> </xsl:template> @@ -981,7 +1032,7 @@ <li><a href="internal_docs.html">Internal Documentation</a></li> </xsl:if> <xsl:if test="boolean(/book/releasenotes)"> - <li><a href="release_notes.html">Release Notes</a></li> + <li><a href="notes.html">Release Notes</a></li> </xsl:if> <xsl:choose> <xsl:when test="string-length($pdfname) > 0"> @@ -1050,17 +1101,16 @@ <!-- Chapter/Section, subsection level 1--> <xsl:template match="chapter/section"> <xsl:param name="chapnum"/> - <h3> - <xsl:for-each select="marker"> - <xsl:call-template name="marker-before-title"/> - </xsl:for-each> - <xsl:call-template name="title_link"> - <xsl:with-param name="title"> - <xsl:value-of select="$chapnum"/>.<xsl:number/>  - <xsl:value-of select="title"/> - </xsl:with-param> - </xsl:call-template> - </h3> + <xsl:for-each select="marker"> + <xsl:call-template name="marker-before-title"/> + </xsl:for-each> + <xsl:call-template name="title_link"> + <xsl:with-param name="title"> + <xsl:value-of select="$chapnum"/>.<xsl:number/>  + <xsl:value-of select="title"/> + </xsl:with-param> + <xsl:with-param name="header" select="'h3'"/> + </xsl:call-template> <xsl:apply-templates> <xsl:with-param name="chapnum" select="$chapnum"/> <xsl:with-param name="sectnum"><xsl:number/></xsl:with-param> @@ -1071,17 +1121,15 @@ <xsl:template match="section/section"> <xsl:param name="chapnum"/> <xsl:param name="sectnum"/> - <h4> - <xsl:for-each select="marker"> - <xsl:call-template name="marker-before-title"/> - </xsl:for-each> - <!-- xsl:value-of select="$partnum"/>.<xsl:value-of select="$chapnum"/>.<xsl:value-of select="$sectnum"/>.<xsl:number/ --> - <xsl:call-template name="title_link"> - <xsl:with-param name="title"> - <xsl:value-of select="title"/> - </xsl:with-param> - </xsl:call-template> - </h4> + <xsl:for-each select="marker"> + <xsl:call-template name="marker-before-title"/> + </xsl:for-each> + <!-- xsl:value-of select="$partnum"/>.<xsl:value-of select="$chapnum"/>.<xsl:value-of select="$sectnum"/>.<xsl:number/ --> + <xsl:call-template name="title_link"> + <xsl:with-param name="title"> + <xsl:value-of select="title"/> + </xsl:with-param> + </xsl:call-template> <xsl:apply-templates> <xsl:with-param name="chapnum" select="$chapnum"/> </xsl:apply-templates> @@ -1106,8 +1154,7 @@ <!-- *ref/Section --> <xsl:template match="erlref/section|cref/section|comref/section|fileref/section|appref/section|funcs/fsdescription"> <xsl:param name="chapnum"/> - <div class="innertube"> - <h3> + <section class="innertube"> <xsl:for-each select="marker"> <xsl:call-template name="marker-before-title"/> </xsl:for-each> @@ -1115,28 +1162,30 @@ <xsl:with-param name="title"> <xsl:value-of select="title"/> </xsl:with-param> + <xsl:with-param name="header" select="'h3'"/> </xsl:call-template> - </h3> - <div class="REFBODY rb-3"> - <xsl:apply-templates> - <xsl:with-param name="chapnum" select="$chapnum"/> - </xsl:apply-templates> - </div> - </div> + <div class="REFBODY rb-3"> + <xsl:apply-templates> + <xsl:with-param name="chapnum" select="$chapnum"/> + </xsl:apply-templates> + </div> + </section> </xsl:template> <!-- *ref/Subsection --> <xsl:template match="erlref/section/section|cref/section/section|comref/section/section|fileref/section/section|appref/section/section"> <xsl:param name="chapnum"/> <xsl:param name="sectnum"/> - <h4> - <xsl:value-of select="title"/> - </h4> - <div class="REFBODY rb-4"> - <xsl:apply-templates> - <xsl:with-param name="chapnum" select="$chapnum"/> - </xsl:apply-templates> - </div> + <section> + <h4 id="{title}"> + <xsl:value-of select="title"/> + </h4> + <div class="REFBODY rb-4"> + <xsl:apply-templates> + <xsl:with-param name="chapnum" select="$chapnum"/> + </xsl:apply-templates> + </div> + </section> </xsl:template> @@ -1445,9 +1494,10 @@ <!-- Menu.internal.chapter --> <xsl:template name="menu.internal.ug"> <xsl:param name="chapnum"/> + <xsl:param name="show"/> - <div id="leftnav"> - <div class="leftnav-tube"> + <aside class="{$show}" id="leftnav"> + <nav class="leftnav-tube"> <xsl:call-template name="erlang_logo"/> @@ -1459,7 +1509,7 @@ <xsl:call-template name="menu_middle"/> - <h3>Chapters</h3> + <h3 id="chapters">Chapters</h3> <ul class="flipMenu" imagepath="{$topdocdir}/js/flipmenu"> <xsl:call-template name="menu.chapter"> @@ -1467,15 +1517,17 @@ <xsl:with-param name="chapnum" select="$chapnum"/> </xsl:call-template> </ul> - </div> - </div> + </nav> + </aside> </xsl:template> <!-- Menu.internal.ref --> <xsl:template name="menu.internal.ref"> <xsl:param name="curModule"/> - <div id="leftnav"> - <div class="leftnav-tube"> + <xsl:param name="show"/> + + <aside class="{$show}" id="leftnav"> + <nav class="leftnav-tube"> <xsl:call-template name="erlang_logo"/> @@ -1487,7 +1539,7 @@ <xsl:call-template name="menu_middle"/> - <h3>Table of Contents</h3> + <h3 id="toc">Table of Contents</h3> <ul class="flipMenu"> <xsl:call-template name="menu.ref2"> @@ -1496,14 +1548,15 @@ <xsl:with-param name="curModule" select="$curModule"/> </xsl:call-template> </ul> - </div> - </div> + </nav> + </aside> </xsl:template> <!-- Menu.internal.chapter combined when we have both modules and free-form chapters --> <xsl:template name="menu.internal.ug_ref"> - <div id="leftnav"> - <div class="leftnav-tube"> + <xsl:param name="show"/> + <aside class="{$show}" id="leftnav"> + <nav class="leftnav-tube"> <xsl:call-template name="erlang_logo"/> <p class="section-title"><xsl:value-of select="/book/header/title"/></p> @@ -1514,7 +1567,7 @@ <xsl:call-template name="menu_middle"/> - <h3>Chapters</h3> + <h3 id="chapters">Chapters</h3> <ul class="flipMenu" imagepath="{$topdocdir}/js/flipmenu"> <xsl:call-template name="menu.chapter"> @@ -1522,7 +1575,7 @@ </xsl:call-template> </ul> - <h3>Modules</h3> + <h3 id="modules">Modules</h3> <ul class="flipMenu"> <xsl:call-template name="menu.ref2"> @@ -1530,8 +1583,8 @@ <!--xsl:with-param name="genFuncMenu" select="true"/--> </xsl:call-template> </ul> - </div> - </div> + </nav> + </aside> </xsl:template> <!--Users Guide --> @@ -1573,9 +1626,10 @@ <!-- Menu.ug --> <xsl:template name="menu.ug"> <xsl:param name="chapnum"/> + <xsl:param name="show"/> - <div id="leftnav"> - <div class="leftnav-tube"> + <aside class="{$show}" id="leftnav"> + <nav class="leftnav-tube"> <xsl:call-template name="erlang_logo"/> @@ -1587,7 +1641,7 @@ <xsl:call-template name="menu_middle"/> - <h3>Chapters</h3> + <h3 id="chapters">Chapters</h3> <ul class="flipMenu" imagepath="{$topdocdir}/js/flipmenu"> <xsl:call-template name="menu.chapter"> @@ -1595,8 +1649,8 @@ <xsl:with-param name="chapnum" select="$chapnum"/> </xsl:call-template> </ul> - </div> - </div> + </nav> + </aside> </xsl:template> @@ -1681,7 +1735,7 @@ <xsl:param name="chapnum"/> <!-- center--> - <h1> + <h1 id="{header/title}"> <xsl:value-of select="$chapnum"/> <xsl:value-of select="header/title"/> </h1> <!-- /center--> @@ -1735,8 +1789,10 @@ <!-- Menu.ref --> <xsl:template name="menu.ref"> <xsl:param name="curModule"/> - <div id="leftnav"> - <div class="leftnav-tube"> + <xsl:param name="show"/> + + <aside class="{$show}" id="leftnav"> + <nav class="leftnav-tube"> <xsl:call-template name="erlang_logo"/> @@ -1748,7 +1804,7 @@ <xsl:call-template name="menu_middle"/> - <h3>Table of Contents</h3> + <h3 id="toc">Table of Contents</h3> <ul class="flipMenu"> <xsl:call-template name="menu.ref2"> @@ -1757,8 +1813,8 @@ <xsl:with-param name="curModule" select="$curModule"/> </xsl:call-template> </ul> - </div> - </div> + </nav> + </aside> </xsl:template> @@ -2266,18 +2322,20 @@ <xsl:template match="description"> <xsl:param name="partnum"/> - <div class="innertube"> - <xsl:call-template name="h3_title_link"> - <xsl:with-param name="title">Description</xsl:with-param> - </xsl:call-template> - <div class="REFBODY description-body"> - <p> - <xsl:apply-templates> - <xsl:with-param name="partnum" select="$partnum"/> - </xsl:apply-templates> - </p> - </div> - </div> + <section class="description"> + <div class="innertube"> + <xsl:call-template name="h3_title_link"> + <xsl:with-param name="title">Description</xsl:with-param> + </xsl:call-template> + <div class="REFBODY description-body"> + <p> + <xsl:apply-templates> + <xsl:with-param name="partnum" select="$partnum"/> + </xsl:apply-templates> + </p> + </div> + </div> + </section> </xsl:template> <!-- Funcs --> @@ -2305,18 +2363,18 @@ <!-- Func --> <xsl:template match="func"> <xsl:param name="partnum"/> - - <xsl:apply-templates select="name"/> - <xsl:apply-templates - select="name[string-length(@arity) > 0 and position()=last()]" - mode="types"/> - - <div class="exports-tube"> - <xsl:apply-templates select="fsummary|type|desc"> - <xsl:with-param name="partnum" select="$partnum"/> - </xsl:apply-templates> - </div> - + <article class="func"> + <xsl:apply-templates select="name"/> + <xsl:apply-templates + select="name[string-length(@arity) > 0 and position()=last()]" + mode="types"/> + + <div class="exports-tube"> + <xsl:apply-templates select="fsummary|type|desc"> + <xsl:with-param name="partnum" select="$partnum"/> + </xsl:apply-templates> + </div> + </article> </xsl:template> <xsl:template match="name"> @@ -2370,23 +2428,13 @@ <xsl:choose> <xsl:when test="ancestor::cref"> - <table class="func-table"> - <tr class="func-tr"> - <td class="cfunc-td"> - <span class="bold_code bc-7"> - <xsl:call-template name="title_link"> - <xsl:with-param name="link" select="substring-before(nametext, '(')"/> - <xsl:with-param name="where" select="'before'"/> - </xsl:call-template> - </span> - </td> - <td class="func-since-td"> - <xsl:if test="string-length(@since) > 0"> - <span class="since"><xsl:value-of select="@since"/></span> - </xsl:if> - </td> - </tr> - </table> + <div class="cref-head bold_code bc-7"> + <xsl:call-template name="title_link"> + <xsl:with-param name="link" select="substring-before(nametext, '(')"/> + <xsl:with-param name="where" select="'before'"/> + <xsl:with-param name="since" select="@since"/> + </xsl:call-template> + </div> </xsl:when> <xsl:when test="ancestor::erlref"> <xsl:variable name="fname"> @@ -2418,26 +2466,16 @@ </div> </xsl:when> <xsl:otherwise> - <table class="func-table"> - <tr class="func-tr"> - <td class="func-td"> - <div class="bold_code fun-type"> - <xsl:call-template name="title_link"> - <xsl:with-param name="link" select="concat(concat($fname,'-'),$arity)"/> - <xsl:with-param name="where" select="'before'"/> - <xsl:with-param name="title"> - <xsl:apply-templates/> - </xsl:with-param> - </xsl:call-template> - </div> - </td> - <td class="func-since-td"> - <xsl:if test="string-length(@since) > 0"> - <span class="since"><xsl:value-of select="@since"/></span> - </xsl:if> - </td> - </tr> - </table> + <div class="bold_code func-head"> + <xsl:call-template name="title_link"> + <xsl:with-param name="link" select="concat(concat($fname,'-'),$arity)"/> + <xsl:with-param name="where" select="'before'"/> + <xsl:with-param name="since" select="@since"/> + <xsl:with-param name="title"> + <xsl:apply-templates/> + </xsl:with-param> + </xsl:call-template> + </div> </xsl:otherwise> </xsl:choose> </xsl:when> @@ -2456,7 +2494,7 @@ <xsl:if test="string-length(@name) = 0 and string-length(@variable) = 0"> <div class="REFBODY rb-5"> - <h3 class="func-types-title">Types</h3> + <h3 id="types" class="func-types-title">Types</h3> <xsl:apply-templates> <xsl:with-param name="partnum" select="$partnum"/> @@ -2492,33 +2530,43 @@ <xsl:template name="h3_title_link"> <xsl:param name="title"/> - <h3> - <xsl:call-template name="title_link"> - <xsl:with-param name="title" select="$title"/> - <xsl:with-param name="link" select="erl:to-link($title)"/> - </xsl:call-template> - </h3> + <xsl:call-template name="title_link"> + <xsl:with-param name="title" select="$title"/> + <xsl:with-param name="link" select="erl:to-link($title)"/> + </xsl:call-template> </xsl:template> <xsl:template name="title_link"> <xsl:param name="title" select="'APPLY'"/> <xsl:param name="link" select="erl:to-link(title)"/> <xsl:param name="where" select="'after'"/> + <xsl:param name="header" select="'h4'"/> + <xsl:param name="since"/> <xsl:param name="ghlink" select="ancestor-or-self::*[@ghlink][position() = 1]/@ghlink"/> <xsl:variable name="id" select="concat(concat($link,'-'), generate-id(.))"/> - <span onMouseOver="document.getElementById('ghlink-{$id}').style.visibility = 'visible';" - onMouseOut="document.getElementById('ghlink-{$id}').style.visibility = 'hidden';"> + + <xsl:element name="{$header}"> + <xsl:attribute name="id"><xsl:value-of select="$link"/></xsl:attribute> + <xsl:attribute name="class">title-link</xsl:attribute> + <xsl:attribute name="onMouseOver"> + <xsl:text>document.getElementById('ghlink-</xsl:text><xsl:value-of select="$id"/><xsl:text>').style.visibility = 'visible';</xsl:text> + </xsl:attribute> + <xsl:attribute name="onMouseOut"> + <xsl:text>document.getElementById('ghlink-</xsl:text><xsl:value-of select="$id"/><xsl:text>').style.visibility = 'hidden';</xsl:text> + </xsl:attribute> <xsl:choose> <xsl:when test="$where = 'before'"> - <xsl:call-template name="ghlink"> - <xsl:with-param name="mfa" select="$link"/> - <xsl:with-param name="id" select="$id"/> - <xsl:with-param name="ghlink" select="$ghlink"/> - <xsl:with-param name="where" select="$where"/> - </xsl:call-template> + <div class="title-anchors"> + <xsl:call-template name="ghlink"> + <xsl:with-param name="mfa" select="$link"/> + <xsl:with-param name="id" select="$id"/> + <xsl:with-param name="ghlink" select="$ghlink"/> + <xsl:with-param name="where" select="$where"/> + </xsl:call-template> + </div> </xsl:when> </xsl:choose> - <a class="title_link" name="{$link}"> + <div class="title-name"> <xsl:choose> <xsl:when test="$title = 'APPLY'"> <xsl:apply-templates/> <!-- like <ret> and <nametext> --> @@ -2527,18 +2575,26 @@ <xsl:copy-of select="$title"/> </xsl:otherwise> </xsl:choose> - </a> + </div> <xsl:choose> <xsl:when test="$where = 'after'"> - <xsl:call-template name="ghlink"> - <xsl:with-param name="mfa" select="$link"/> - <xsl:with-param name="id" select="$id"/> - <xsl:with-param name="ghlink" select="$ghlink"/> - <xsl:with-param name="where" select="$where"/> - </xsl:call-template> + <div class="title-anchors"> + <xsl:call-template name="ghlink"> + <xsl:with-param name="mfa" select="$link"/> + <xsl:with-param name="id" select="$id"/> + <xsl:with-param name="ghlink" select="$ghlink"/> + <xsl:with-param name="where" select="$where"/> + </xsl:call-template> + </div> </xsl:when> </xsl:choose> - </span> + <xsl:if test="string-length($since) > 0"> + <div class="title-since"> + <span class="since"><xsl:value-of select="$since"/> + </span> + </div> + </xsl:if> + </xsl:element> </xsl:template> <xsl:template name="ghlink"> @@ -2566,11 +2622,9 @@ <xsl:template match="desc"> <xsl:param name="partnum"/> <div class="REFBODY rb-7"> - <p> - <xsl:apply-templates> - <xsl:with-param name="partnum" select="$partnum"/> - </xsl:apply-templates> - </p> + <xsl:apply-templates> + <xsl:with-param name="partnum" select="$partnum"/> + </xsl:apply-templates> </div> </xsl:template> @@ -2784,9 +2838,10 @@ <!-- Menu.rn --> <xsl:template name="menu.rn"> <xsl:param name="chapnum"/> + <xsl:param name="show"/> - <div id="leftnav"> - <div class="leftnav-tube"> + <aside class="{$show}" id="leftnav"> + <nav class="leftnav-tube"> <xsl:call-template name="erlang_logo"/> @@ -2798,7 +2853,7 @@ <xsl:call-template name="menu_middle"/> - <h3>Chapters</h3> + <h3 id="chapters">Chapters</h3> <ul class="flipMenu" imagepath="{$topdocdir}/js/flipmenu"> <xsl:call-template name="menu.chapter"> @@ -2806,8 +2861,8 @@ <xsl:with-param name="chapnum" select="$chapnum"/> </xsl:call-template> </ul> - </div> - </div> + </nav> + </aside> </xsl:template> <!-- Special templates to calculate the arity of functions --> diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 1c17004aa0..68d1984c7f 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -1189,6 +1189,18 @@ logger:set_proxy_config(maps:merge(Old, Config)). </desc> </func> + <func> + <name name="reconfigure" arity="0" since="OTP @OTP-17375@"/> + <fsummary>Reconfigure Logger.</fsummary> + <desc> + <p>Reconfigure Logger using updated <c>kernel</c> configuration + that was set after <c>kernel</c> application was loaded.</p> + <p>Beware, that this is meant to be run only by the build tools, + not manually during application lifetime, as this may cause + missing log entries.</p> + </desc> + </func> + </funcs> diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index ea75c8d720..9107bf0e61 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -51,9 +51,9 @@ get_primary_config/0, get_handler_config/1, get_handler_config/0, get_handler_ids/0, get_config/0, get_proxy_config/0, - add_handlers/1]). + add_handlers/1, + reconfigure/0]). -%% Private configuration -export([internal_init_logger/0]). %% Misc @@ -794,6 +794,31 @@ print_module_levels(Modules,M) -> [print_module_levels(Module,M) || Module <- Modules], ok. +-spec reconfigure() -> ok | {error,term()}. +%% This function is meant to be used by the build tools like Rebar3 or Mix +%% to ensure that the logger configuration is reset to the expected state +%% before running main application. +reconfigure() -> + try + [case logger:remove_handler(Id) of + ok -> ok; + {error, Reason} -> throw({remove, Id, Reason}) + end || #{id := Id} <- logger:get_handler_config()], + ok=logger:add_handler(simple,logger_simple_h, + #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS}), + logger:unset_module_level(), + internal_init_logger() + of + ok -> + logger:add_handlers(kernel); + Error -> + Error + catch + throw:Reason -> + {error, Reason} + end. + -spec internal_init_logger() -> ok | {error,term()}. %% This function is responsible for config of the logger %% This is done before add_handlers because we want the @@ -807,6 +832,7 @@ internal_init_logger() -> ok = logger:set_primary_config(metadata, get_primary_metadata()), ok = logger:set_primary_config(filter_default, get_primary_filter_default(Env)), + ok = logger:set_primary_config(filters, []), [case logger:add_primary_filter(Id, Filter) of ok -> ok; diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 82533ab572..5545759b5d 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -24,6 +24,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/logger.hrl"). -include_lib("kernel/src/logger_internal.hrl"). +-include_lib("stdlib/include/assert.hrl"). -define(str,"Log from "++atom_to_list(?FUNCTION_NAME)++ ":"++integer_to_list(?LINE)). @@ -1009,15 +1010,14 @@ app_config(Config) -> %% start, it is not possible to see code coverage in that test. kernel_config(Config) -> %% Start a node with simple handler only, then simulate kernel - %% start by calling internally exported - %% internal_init_logger(). This is to test all variants of kernel - %% config, including bad config, and see the code coverage. + %% start by calling logger:reconfigure(). This is to test all + %% variants of kernel config, including bad config, and see + %% the code coverage. {ok,#{handlers:=[#{id:=simple,filters:=DF}]}=LC,Node} = logger_test_lib:setup(Config,[{error_logger,false}]), %% Same once more, to get coverage ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), LC = rpc:call(Node,logger,get_config,[]), %% This shall mean the same as above, but using 'logger' parameter @@ -1025,15 +1025,13 @@ kernel_config(Config) -> ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), ok = rpc:call(Node,application,set_env, [kernel,logger,[{handler,default,undefined}]]), - ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), - LC = rpc:call(Node,logger,get_config,[]), + ok = rpc:call(Node,logger,reconfigure,[]), + ?assertEqual(LC, rpc:call(Node,logger,get_config,[])), %% Silent ok = rpc:call(Node,application,unset_env,[kernel,logger]), ok = rpc:call(Node,application,set_env,[kernel,error_logger,silent]), - ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), + ok = rpc:call(Node,logger,reconfigure,[]), #{primary:=#{filter_default:=log,filters:=[]}, handlers:=[], module_levels:=[]} = rpc:call(Node,logger,get_config,[]), @@ -1041,30 +1039,25 @@ kernel_config(Config) -> %% Default ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), ok = rpc:call(Node,application,unset_env,[kernel,logger]), - ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), + ok = rpc:call(Node,logger,reconfigure,[]), #{primary:=#{filter_default:=log,filters:=[]}, handlers:=[#{id:=default,filters:=DF,config:=#{type:=standard_io}}], module_levels:=[]} = rpc:call(Node,logger,get_config,[]), %% error_logger=tty (same as default) - ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again ok = rpc:call(Node,application,set_env,[kernel,error_logger,tty]), ok = rpc:call(Node,application,unset_env,[kernel,logger]), - ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), + ok = rpc:call(Node,logger,reconfigure,[]), #{primary:=#{filter_default:=log,filters:=[]}, handlers:=[#{id:=default,filters:=DF,config:=#{type:=standard_io}}], module_levels:=[]} = rpc:call(Node,logger,get_config,[]), %% error_logger={file,File} - ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again F = filename:join(?config(priv_dir,Config), atom_to_list(?FUNCTION_NAME)++".log"), ok = rpc:call(Node,application,set_env,[kernel,error_logger,{file,F}]), ok = rpc:call(Node,application,unset_env,[kernel,logger]), - ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), + ok = rpc:call(Node,logger,reconfigure,[]), #{primary:=#{filter_default:=log,filters:=[]}, handlers:=[#{id:=default,filters:=DF, config:=#{type:=file,file:=F,modes:=Modes}}], @@ -1073,55 +1066,47 @@ kernel_config(Config) -> %% Same, but using 'logger' parameter instead of 'error_logger' - ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), ok = rpc:call(Node,application,set_env,[kernel,logger, [{handler,default,logger_std_h, #{config=>#{type=>{file,F}}}}]]), - ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), + ok = rpc:call(Node,logger,reconfigure,[]), #{primary:=#{filter_default:=log,filters:=[]}, handlers:=[#{id:=default,filters:=DF, config:=#{type:=file,file:=F,modes:=Modes}}], module_levels:=[]} = rpc:call(Node,logger,get_config,[]), %% Same, but with type={file,File,Modes} - ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), M = [raw,write], ok = rpc:call(Node,application,set_env,[kernel,logger, [{handler,default,logger_std_h, #{config=>#{type=>{file,F,M}}}}]]), - ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), + ok = rpc:call(Node,logger,reconfigure,[]), #{primary:=#{filter_default:=log,filters:=[]}, handlers:=[#{id:=default,filters:=DF, config:=#{type:=file,file:=F,modes:=[delayed_write|M]}}], module_levels:=[]} = rpc:call(Node,logger,get_config,[]), %% Same, but with disk_log handler - ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), ok = rpc:call(Node,application,set_env,[kernel,logger, [{handler,default,logger_disk_log_h, #{config=>#{file=>F}}}]]), - ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), + ok = rpc:call(Node,logger,reconfigure,[]), #{primary:=#{filter_default:=log,filters:=[]}, handlers:=[#{id:=default,filters:=DF,config:=#{file:=F}}], module_levels:=[]} = rpc:call(Node,logger,get_config,[]), %% Set primary filters and module level. No default handler. - ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), ok = rpc:call(Node,application,set_env, [kernel,logger,[{handler,default,undefined}, {filters,stop,[{f1,{fun(_,_) -> log end,ok}}]}, {module_level,debug,[?MODULE]}]]), - ok = rpc:call(Node,logger,internal_init_logger,[]), - ok = rpc:call(Node,logger,add_handlers,[kernel]), + ok = rpc:call(Node,logger,reconfigure,[]), #{primary:=#{filter_default:=stop,filters:=[_]}, - handlers:=[], + handlers:=[#{id:=simple}], module_levels:=[{?MODULE,debug}]} = rpc:call(Node,logger,get_config,[]), %% Bad config @@ -1129,38 +1114,38 @@ kernel_config(Config) -> ok = rpc:call(Node,application,set_env,[kernel,error_logger,bad]), {error,{bad_config,{kernel,{error_logger,bad}}}} = - rpc:call(Node,logger,internal_init_logger,[]), + rpc:call(Node,logger,reconfigure,[]), ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), ok = rpc:call(Node,application,set_env,[kernel,logger_level,bad]), {error,{bad_config,{kernel,{logger_level,bad}}}} = - rpc:call(Node,logger,internal_init_logger,[]), + rpc:call(Node,logger,reconfigure,[]), ok = rpc:call(Node,application,unset_env,[kernel,logger_level]), ok = rpc:call(Node,application,set_env, [kernel,logger,[{filters,stop,[bad]}]]), {error,{bad_config,{kernel,{invalid_filters,[bad]}}}} = - rpc:call(Node,logger,internal_init_logger,[]), + rpc:call(Node,logger,reconfigure,[]), ok = rpc:call(Node,application,set_env, [kernel,logger,[{filters,stop,[bad]}]]), {error,{bad_config,{kernel,{invalid_filters,[bad]}}}} = - rpc:call(Node,logger,internal_init_logger,[]), + rpc:call(Node,logger,reconfigure,[]), ok = rpc:call(Node,application,set_env, [kernel,logger,[{filters,stop,[{f1,bad}]}]]), {error,{bad_config,{kernel,{invalid_filter,{f1,bad}}}}} = - rpc:call(Node,logger,internal_init_logger,[]), + rpc:call(Node,logger,reconfigure,[]), ok = rpc:call(Node,application,set_env, [kernel,logger,MF=[{filters,stop,[]},{filters,log,[]}]]), {error,{bad_config,{kernel,{multiple_filters,MF}}}} = - rpc:call(Node,logger,internal_init_logger,[]), + rpc:call(Node,logger,reconfigure,[]), ok = rpc:call(Node,application,set_env, [kernel,logger,[{module_level,bad,[?MODULE]}]]), {error,{bad_config,{kernel,{invalid_level,bad}}}} = - rpc:call(Node,logger,internal_init_logger,[]), + rpc:call(Node,logger,reconfigure,[]), ok. diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml index f39d64b6ab..7fe31c08d8 100644 --- a/lib/mnesia/doc/src/mnesia.xml +++ b/lib/mnesia/doc/src/mnesia.xml @@ -1464,14 +1464,6 @@ mnesia:create_table(person, </desc> </func> <func> - <name name="install_fallback" arity="1" since=""/> - <fsummary>Installs a backup as fallback.</fsummary> - <desc> - <p>Calls <c>mnesia:install_fallback(Opaque, Args)</c>, where - <c>Args</c> is <c>[{scope, global}, {module, BackupMod}]</c>.</p> - </desc> - </func> - <func> <name name="install_fallback" arity="2" since=""/> <fsummary>Installs a backup as fallback.</fsummary> <desc> @@ -2645,8 +2637,8 @@ mnesia:create_table(employee, add_family({family, F, M, Children}) -> ChildOids = lists:map(fun oid/1, Children), Trans = fun() -> - mnesia:write(F#person{children = ChildOids}, - mnesia:write(M#person{children = ChildOids}, + mnesia:write(F#person{children = ChildOids}), + mnesia:write(M#person{children = ChildOids}), Write = fun(Child) -> mnesia:write(Child) end, lists:foreach(Write, Children) end, diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index b7e7f98499..b0b4ac023f 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -180,7 +180,7 @@ do_get_disc_copy2(Tab, Reason, Storage = {ext, Alias, Mod}, _Type) -> %% %% Grab read lock on table %% Block dirty updates -%% Update wherabouts +%% Update whereabouts %% %% Cancel the update subscription %% Process the subscription events diff --git a/lib/snmp/src/app/snmp_app.erl b/lib/snmp/src/app/snmp_app.erl index dbc1ec0b6d..486b276383 100644 --- a/lib/snmp/src/app/snmp_app.erl +++ b/lib/snmp/src/app/snmp_app.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2021. 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. @@ -155,7 +155,7 @@ stop() -> get_env() -> Env = application:get_all_env(snmp), - DeleteElem = [included_applications], + DeleteElem = [included_applications, test_inet_backends], F = fun({Key, _}) -> lists:member(Key, DeleteElem) end, lists:dropwhile(F, Env). diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml index cc74067ecf..3673372fdc 100644 --- a/lib/ssl/doc/src/standards_compliance.xml +++ b/lib/ssl/doc/src/standards_compliance.xml @@ -166,7 +166,7 @@ </cell> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"><em>@OTP-16590@</em></cell> + <cell align="left" valign="middle"><em>24.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -178,7 +178,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">RSASSA-PSS signature schemes</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"><em>@OTP-16590@</em></cell> + <cell align="left" valign="middle"><em>24.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -190,7 +190,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert extension</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"><em>@OTP-16590@</em></cell> + <cell align="left" valign="middle"><em>24.1</em></cell> </row> <row> diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml index fae2ca80e7..257f260d31 100644 --- a/lib/ssl/doc/src/using_ssl.xml +++ b/lib/ssl/doc/src/using_ssl.xml @@ -169,7 +169,7 @@ ok</code> </code> <p>In OTP 20 it is desirable to remove all cipher suites - that uses rsa kexchange (removed from default in 21) </p> + that uses rsa key exchange (removed from default in 21) </p> <code type="erl">2> NoRSA = ssl:filter_cipher_suites(Default, [{key_exchange, fun(rsa) -> false; @@ -354,7 +354,7 @@ ok <p>Step 3 - Explicit Session Reuse </p> <code type="erl"> -%% Preform a full handshake and the session will not be saved for reuse +%% Perform a full handshake and the session will not be saved for reuse 12> {ok, C9} = ssl:connect("localhost", 9999, [{verify, verify_peer}, {versions, ['tlsv1.2']}, {cacertfile, "cacerts.pem"}, @@ -433,7 +433,7 @@ ok <seeguide marker="ssl:using_ssl#anti-replay-protection-in-tls-1.3"> Anti-Replay Protection in TLS 1.3</seeguide> </p> - <p>Session tickets are sent by servers on newly estalished TLS connections. + <p>Session tickets are sent by servers on newly established TLS connections. The number of tickets sent and their lifetime are configurable by application variables. See also <seeapp marker="ssl:ssl_app#configuration"> SSL's configuration</seeapp>.</p> <p>Session tickets are protected by application traffic keys, and in stateless diff --git a/lib/ssl/test/tls_api_SUITE.erl b/lib/ssl/test/tls_api_SUITE.erl index 229ba5c19d..453ccf12ee 100644 --- a/lib/ssl/test/tls_api_SUITE.erl +++ b/lib/ssl/test/tls_api_SUITE.erl @@ -180,6 +180,7 @@ init_per_testcase(_, Config) -> Config. end_per_testcase(_TestCase, Config) -> Config. + %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- diff --git a/lib/stdlib/src/shell_docs.erl b/lib/stdlib/src/shell_docs.erl index 5aaeedde52..f4be20e26c 100644 --- a/lib/stdlib/src/shell_docs.erl +++ b/lib/stdlib/src/shell_docs.erl @@ -590,6 +590,8 @@ normalize_format(Docs, #docs_v1{ format = <<"text/", _/binary>> }) when is_binar %%% Functions for rendering reference documentation render_function([], _D, _Config) -> {error,function_missing}; +render_function(FDocs, D, Config) when is_map(Config) -> + render_function(FDocs, D, init_config(D, Config)); render_function(FDocs, #docs_v1{ docs = Docs } = D, Config) -> Grouping = lists:foldl( @@ -602,7 +604,7 @@ render_function(FDocs, #docs_v1{ docs = Docs } = D, Config) -> end, #{}, lists:sort(FDocs)), lists:map( fun({Group,Members}) -> - Signatures = lists:flatmap(fun render_signature/1,lists:reverse(Members)), + Signatures = lists:flatmap(fun render_signature/1, lists:reverse(Members)), case lists:search(fun({_,_,_,Doc,_}) -> Doc =/= #{} end, Members) of @@ -625,7 +627,7 @@ render_function(FDocs, #docs_v1{ docs = Docs } = D, Config) -> render_signature({{_Type,_F,_A},_Anno,_Sigs,_Docs,#{ signature := Specs } = Meta}) -> lists:flatmap( fun(ASTSpec) -> - PPSpec = erl_pp:attribute(ASTSpec,[{encoding,utf8}]), + PPSpec = erl_pp:attribute(ASTSpec,[{encoding,unicode}]), Spec = case ASTSpec of {_Attribute, _Line, opaque, _} -> @@ -672,6 +674,8 @@ render_headers_and_docs(Headers, DocContents, #config{} = Config) -> render_docs(DocContents, 2, Config)]. %%% Functions for rendering type/callback documentation +render_signature_listing(Module, Type, D, Config) when is_map(Config) -> + render_signature_listing(Module, Type, D, init_config(D, Config)); 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}) -> @@ -705,7 +709,7 @@ render_typecb_docs(Docs, 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, D, Config) when is_record(D, docs_v1) -> render_docs(DocContents, 0, init_config(D, Config)); render_docs(DocContents, Ind, D = #config{}) when is_integer(Ind) -> init_ansi(D), @@ -716,7 +720,7 @@ render_docs(DocContents, Ind, D = #config{}) when is_integer(Ind) -> clean_ansi() end. -init_config(D, Config) -> +init_config(D, Config) when is_map(Config) -> DefaultOpts = io:getopts(), DefaultEncoding = proplists:get_value(encoding, DefaultOpts, latin1), Columns = @@ -735,7 +739,9 @@ init_config(D, Config) -> encoding = maps:get(encoding, Config, DefaultEncoding), ansi = maps:get(ansi, Config, undefined), columns = Columns - }. + }; +init_config(D, Config) -> + Config#config{ docs = D }. render_docs(Elems,State,Pos,Ind,D) when is_list(Elems) -> lists:mapfoldl(fun(Elem,P) -> @@ -873,13 +879,13 @@ render_element({dt,_,Content},[dl | _] = State,Pos,Ind,D) -> 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) -> +render_element(B, State, Pos, Ind, D) 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) + render_words(split_to_words(B),State,Pos,Ind,[[]],D) end; render_element({Tag,Attr,Content}, State, Pos, Ind,D) -> @@ -892,33 +898,42 @@ render_element({Tag,Attr,Content}, State, Pos, Ind,D) -> end, render_docs(Content, State, Pos, Ind,D). -render_words(Words,[_,types|State],Pos,Ind,Acc,Cols) -> +render_words(Words,[_,types|State],Pos,Ind,Acc,D) -> %% 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) -> + render_words(Words,State,Pos,Ind+2,Acc,D); +render_words([UnicodeWord|T],State,Pos,Ind,Acc,#config{ columns = Cols } = D) + when is_binary(UnicodeWord) -> + Word = translate(UnicodeWord, D), 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])), + IsPunct = re:run(Word,"^\\W$",[unicode]) =/= nomatch, + 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); + render_words(T,State,WordLength+Ind+1,Ind,[[[nlpad(Ind), Word]]|Acc],D); 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) + render_words(T,State,NewPosSpc,Ind,[[Word|Line]|LineAcc],D) end; -render_words([],_State,Pos,_Ind,Acc,_Cols) -> +render_words([],_State,Pos,_Ind,Acc,_D) -> Lines = lists:map(fun(RevLine) -> Line = lists:reverse(RevLine), lists:join($ ,Line) end,lists:reverse(Acc)), {iolist_to_binary(Lines), Pos}. +%% If the encoding is not unicode, we translate all nbsp to sp +translate(UnicodeWord, #config{ encoding = unicode }) -> + UnicodeWord; +translate(UnicodeWord, #config{ encoding = latin1 }) -> + string:replace(UnicodeWord, [160], " ", all). + render_type_signature(Name, #config{ docs = #docs_v1{ metadata = #{ types := AllTypes }}}) -> case [Type || Type = {TName,_} <- maps:keys(AllTypes), TName =:= Name] of [] -> diff --git a/lib/stdlib/test/shell_docs_SUITE.erl b/lib/stdlib/test/shell_docs_SUITE.erl index 06a564620e..4f83fb7047 100644 --- a/lib/stdlib/test/shell_docs_SUITE.erl +++ b/lib/stdlib/test/shell_docs_SUITE.erl @@ -118,29 +118,34 @@ render_smoke(_Config) -> 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), + E = fun({error,_}) -> + ok; + (Doc) -> + unicode:characters_to_binary(Doc) + end, + E(shell_docs:render(Mod, D, Config)), + E(shell_docs:render_type(Mod, D, Config)), + E(shell_docs:render_callback(Mod, D, Config)), Exports = try Mod:module_info(exports) catch _:undef -> [] end, %% nif file not available on this platform [try - shell_docs:render(Mod, F, A, D, Config) + E(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} <- Exports], [try - shell_docs:render_type(Mod, T, A, D, Config) + E(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) + E(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)]), @@ -155,8 +160,8 @@ render_smoke(_Config) -> #{ ansi => true }, #{ columns => 5 }, #{ columns => 150 }, - #{ encoding => unicode}, - #{ encoding => latin1}]) + #{ encoding => unicode }, + #{ encoding => latin1 }]) end), ok. diff --git a/make/otp_release_targets.mk b/make/otp_release_targets.mk index a2ba51c9cf..f525368d7a 100644 --- a/make/otp_release_targets.mk +++ b/make/otp_release_targets.mk @@ -187,11 +187,15 @@ local_docs: local_copy_of_topdefs docs local_html: TOPDOCDIR=. local_html: local_copy_of_topdefs html -local_copy_of_topdefs: - $(INSTALL) $(DOCGEN)/priv/css/otp_doc.css $(HTMLDIR) +$(HTMLDIR)/otp_doc.css: + ln -s $(DOCGEN)/priv/css/otp_doc.css $(HTMLDIR)/otp_doc.css + +local_copy_of_topdefs: $(HTMLDIR)/otp_doc.css + cd $(ERL_TOP) && make mod2app $(INSTALL) $(DOCGEN)/priv/images/erlang-logo.png $(HTMLDIR) $(INSTALL) $(DOCGEN)/priv/images/erlang-logo.gif $(HTMLDIR) $(INSTALL_DIR) $(HTMLDIR)/js/flipmenu + $(INSTALL) $(DOCGEN)/priv/js/*.js $(HTMLDIR)/js $(INSTALL) $(DOCGEN)/priv/js/flipmenu/flip_closed.gif \ $(DOCGEN)/priv/js/flipmenu/flip_open.gif \ $(DOCGEN)/priv/js/flipmenu/flip_static.gif \ diff --git a/make/otp_version_tickets b/make/otp_version_tickets index 553ca835d2..b8220e1a87 100644 --- a/make/otp_version_tickets +++ b/make/otp_version_tickets @@ -1,8 +1 @@ -OTP-17393 -OTP-17657 -OTP-17658 -OTP-17659 -OTP-17666 -OTP-17668 -OTP-17670 -OTP-17672 +DEVELOPMENT diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge index e69de29bb2..553ca835d2 100644 --- a/make/otp_version_tickets_in_merge +++ b/make/otp_version_tickets_in_merge @@ -0,0 +1,8 @@ +OTP-17393 +OTP-17657 +OTP-17658 +OTP-17659 +OTP-17666 +OTP-17668 +OTP-17670 +OTP-17672 diff --git a/scripts/otp_html_check b/scripts/otp_html_check index 893d2b55f1..3606ed22d4 100755 --- a/scripts/otp_html_check +++ b/scripts/otp_html_check @@ -226,7 +226,7 @@ sub get_page_links { ($html =~ m/ < \s* - A + (?: A|H[1-6]) [^>]* \s (?: NAME|ID) \s* = \s* (?: \"([^\"]*)\" | \'([^\']*)\' | ([^>\s]+) ) diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile index 3ebee96c08..803f907994 100644 --- a/system/doc/top/Makefile +++ b/system/doc/top/Makefile @@ -260,9 +260,9 @@ $(TOP_PDF_FILE): $(XML_FILES) pdf: $(TOP_PDF_FILE) -html: $(INDEX_FILES) $(JAVASCRIPT) +html: $(INDEX_FILES) $(JAVASCRIPT) $(MAN_INDEX) -man: $(MAN_INDEX) +man: debug opt: @@ -287,10 +287,10 @@ $(RELSYSDIR)/docbuild: $(INSTALL_DIR) $(RELSYSDIR)/docbuild release_man_spec: man $(RELSYSDIR)/docbuild - $(INSTALL_DATA) $(MAN_INDEX) $(RELSYSDIR) - $(INSTALL_DATA) $(MAN_INDEX_SRC) $(MAN_INDEX_SCRIPT) $(RELSYSDIR)/docbuild release_html_spec: html $(RELSYSDIR)/docbuild + $(INSTALL_DATA) $(MAN_INDEX) $(RELSYSDIR) + $(INSTALL_DATA) $(MAN_INDEX_SRC) $(MAN_INDEX_SCRIPT) $(RELSYSDIR)/docbuild $(INSTALL_DIR) $(RELSYSDIR)/js $(INSTALL_DATA) $(JAVASCRIPT) $(RELSYSDIR)/js $(INSTALL_DATA) $(INDEX_FILES) $(RELSYSDIR) diff --git a/system/doc/top/templates/index.html.src b/system/doc/top/templates/index.html.src index ec53a5b3c5..0c899efe0c 100644 --- a/system/doc/top/templates/index.html.src +++ b/system/doc/top/templates/index.html.src @@ -20,20 +20,45 @@ limitations under the License. --> <html> -<head> + <head> + <meta name="viewport" content="width=device-width, initial-scale=1"></meta> + <meta charset="utf-8"></meta> <link rel="stylesheet" href="otp_doc.css" type="text/css"/> <title>Erlang/OTP #otp_base_vsn#</title> <script id="js" type="text/javascript" language="JavaScript" src="js/flipmenu/flipmenu.js"> - +</script> +<script id="js2" type="text/javascript" language="JavaScript" src="js/topbar.js"> </script> </head> <body> - -<div id="container"> -<div id="leftnav"> -<div class="leftnav-tube"> + <div id="container"> + <div class="topbar"> + <div class="topbar-expand"> + <button onclick="toggleDisplay();"> + <svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" width="24" height="24"> +<g> + <path style="fill:#000000;" d="M27,54c-0.552,0-1-0.448-1-1V8c0-0.552,0.448-1,1-1s1,0.448,1,1v45C28,53.552,27.552,54,27,54z"/> + <path style="fill:#000000;" d="M11,25c-0.256,0-0.512-0.098-0.707-0.293c-0.391-0.391-0.391-1.023,0-1.414l16-16 + c0.391-0.391,1.023-0.391,1.414,0s0.391,1.023,0,1.414l-16,16C11.512,24.902,11.256,25,11,25z"/> + <path style="fill:#000000;" d="M43,25c-0.256,0-0.512-0.098-0.707-0.293l-16-16c-0.391-0.391-0.391-1.023,0-1.414 + s1.023-0.391,1.414,0l16,16c0.391,0.391,0.391,1.023,0,1.414C43.512,24.902,43.256,25,43,25z"/> + <path style="fill:#000000;" d="M43,2H11c-0.552,0-1-0.448-1-1s0.448-1,1-1h32c0.552,0,1,0.448,1,1S43.552,2,43,2z"/> +</g> + </svg> + </button> + </div> + <div class="topbar-title"> + <h1> + Erlang/OTP #otp_base_vsn# + </h1> + </div> + <div class="search-expand"> + </div> + </div> +<aside id="leftnav"> +<nav class="leftnav-tube"> <div class="erlang-logo-wrapper"> <img alt="Erlang logo" src="erlang-logo.png" class="erlang-logo"/ > </div> @@ -78,8 +103,8 @@ limitations under the License. #applinks# </ul> -</div> -</div> +</nav> +</aside> <div id="content"> @@ -92,7 +117,18 @@ limitations under the License. <h2>Some hints that may get you started faster</h2> <ul> - + <li class="mobile-only"> + The navigation menu can be accessed by clicking the <svg style="transform: rotate(180deg);" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54" width="24" height="24"> +<g> + <path style="fill:#000000;" d="M27,54c-0.552,0-1-0.448-1-1V8c0-0.552,0.448-1,1-1s1,0.448,1,1v45C28,53.552,27.552,54,27,54z"/> + <path style="fill:#000000;" d="M11,25c-0.256,0-0.512-0.098-0.707-0.293c-0.391-0.391-0.391-1.023,0-1.414l16-16 + c0.391-0.391,1.023-0.391,1.414,0s0.391,1.023,0,1.414l-16,16C11.512,24.902,11.256,25,11,25z"/> + <path style="fill:#000000;" d="M43,25c-0.256,0-0.512-0.098-0.707-0.293l-16-16c-0.391-0.391-0.391-1.023,0-1.414 + s1.023-0.391,1.414,0l16,16c0.391,0.391,0.391,1.023,0,1.414C43.512,24.902,43.256,25,43,25z"/> + <path style="fill:#000000;" d="M43,2H11c-0.552,0-1-0.448-1-1s0.448-1,1-1h32c0.552,0,1,0.448,1,1S43.552,2,43,2z"/> +</g> + </svg> symbol in the top left corner. + </li> <li> The Erlang language is described in the <a href="reference_manual/users_guide.html">Erlang Reference Manual</a>. |